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.seam;
022
023 import java.lang.annotation.Annotation;
024 import java.lang.reflect.Field;
025 import java.util.ArrayList;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.Set;
029
030 import org.granite.tide.TidePersistenceManager;
031 import org.granite.tide.annotations.BypassTideInterceptor;
032 import org.granite.tide.invocation.ContextUpdate;
033 import org.granite.tide.seam.TideInit.FactoryVariable;
034 import org.granite.tide.seam.lazy.SeamInitializer;
035 import org.granite.tide.seam.lazy.TidePersistenceFactory;
036 import org.jboss.seam.Component;
037 import org.jboss.seam.ScopeType;
038 import org.jboss.seam.Component.BijectedAttribute;
039 import org.jboss.seam.annotations.DataBinderClass;
040 import org.jboss.seam.annotations.In;
041 import org.jboss.seam.annotations.Out;
042 import org.jboss.seam.annotations.intercept.AroundInvoke;
043 import org.jboss.seam.annotations.intercept.Interceptor;
044 import org.jboss.seam.annotations.security.Restrict;
045 import org.jboss.seam.bpm.BusinessProcessInterceptor;
046 import org.jboss.seam.contexts.Context;
047 import org.jboss.seam.contexts.Contexts;
048 import org.jboss.seam.core.BijectionInterceptor;
049 import org.jboss.seam.core.EventInterceptor;
050 import org.jboss.seam.databinding.DataBinder;
051 import org.jboss.seam.intercept.AbstractInterceptor;
052 import org.jboss.seam.intercept.InvocationContext;
053 import org.jboss.seam.log.LogProvider;
054 import org.jboss.seam.log.Logging;
055
056
057 /**
058 * This interceptor has 4 activities :
059 * - Updating the context with data received from the Flex client, remerging client data in the persistence context when needed
060 * - Intercept outjected values to return it to the client
061 * - Determine the Persistence Context being used for the conversation and creating a lazyinitializer
062 * storing it in the current conversation
063 * - Return all changed values to the client
064 *
065 * @author Venkat DANDA
066 * @author Cameron INGRAM
067 * @author William DRAI
068 */
069
070
071 @Interceptor(around={BijectionInterceptor.class, EventInterceptor.class, BusinessProcessInterceptor.class})
072 public class TideInterceptor extends AbstractInterceptor {
073
074 private static final long serialVersionUID = 1L;
075
076
077 private static final LogProvider log = Logging.getLogProvider(TideInterceptor.class);
078
079 private boolean reentrant; //OK, since all Seam components are single-threaded
080
081
082 @AroundInvoke
083 @SuppressWarnings({ "unchecked", "rawtypes" })
084 public Object aroundInvoke(InvocationContext invocation) throws Exception {
085
086 if (reentrant) {
087 log.trace("About to invoke method");
088
089 if (log.isTraceEnabled())
090 log.trace("reentrant call to component: " + getComponent().getName() );
091
092 Object result = invocation.proceed();
093
094 log.trace("Method invoked");
095
096 return result;
097 }
098
099 reentrant = true;
100
101 try {
102 if (getComponent().getBeanClass().isAnnotationPresent(BypassTideInterceptor.class))
103 return invocation.proceed();
104
105 TideInvocation tideInvocation = TideInvocation.get();
106
107 if (tideInvocation == null || tideInvocation.isLocked())
108 return invocation.proceed();
109
110 AbstractSeamServiceContext tideContext = null;
111 if (Contexts.isSessionContextActive())
112 tideContext = (AbstractSeamServiceContext)Component.getInstance(AbstractSeamServiceContext.COMPONENT_NAME, true);
113
114 if (tideContext == null)
115 return invocation.proceed();
116
117 // Ignore lifecycle methods
118 if (SeamUtils.isLifecycleMethod(getComponent(), invocation.getMethod())) {
119 tideInvocation.lock();
120
121 Object result = invocation.proceed();
122
123 tideInvocation.unlock();
124
125 return result;
126 }
127
128
129 boolean evaluate = false;
130
131 //Check for persistence
132 checkForPersistenceContexts(invocation);
133
134 // Ignore inner interceptions of other components during processing
135 if (tideInvocation.isEnabled() && !tideInvocation.isUpdated()) {
136 List<ContextUpdate> updates = new ArrayList<ContextUpdate>(tideInvocation.getUpdates());
137 tideInvocation.updated();
138 tideContext.restoreContext(updates, getComponent(), invocation.getTarget());
139 evaluate = true;
140
141 // Inject DataModel selections
142 Field field = getComponent().getClass().getDeclaredField("dataModelGetters");
143 field.setAccessible(true);
144 List<BijectedAttribute<?>> dataModelGetters = (List<BijectedAttribute<?>>)field.get(getComponent());
145 for (BijectedAttribute<?> getter : dataModelGetters) {
146 Annotation dataModelAnn = getter.getAnnotation();
147 DataBinder wrapper = dataModelAnn.annotationType().getAnnotation(DataBinderClass.class).value().newInstance();
148 String name = getter.getName();
149 ScopeType scope = wrapper.getVariableScope(dataModelAnn);
150 if (scope == ScopeType.UNSPECIFIED) {
151 scope = getComponent().getScope();
152 if (scope == ScopeType.STATELESS)
153 scope = ScopeType.EVENT;
154 }
155 Object dataModel = scope.getContext().get(name);
156 if (dataModel != null && dataModel instanceof TideDataModel) {
157 Field field2 = getComponent().getClass().getDeclaredField("dataModelSelectionSetters");
158 field2.setAccessible(true);
159 Map<String, BijectedAttribute<?>> setters = (Map<String, BijectedAttribute<?>>)field2.get(getComponent());
160 BijectedAttribute setter = setters.get(name);
161 if (setter != null) {
162 Object value = setter.get(invocation.getTarget());
163 ((TideDataModel)dataModel).setRowData(value);
164 }
165 }
166 }
167 }
168
169 // Do invocation
170 Object result = invocation.proceed();
171
172 boolean restrict = getComponent().beanClassHasAnnotation(Restrict.class);
173
174 // Intercept outjected values
175 if (getComponent().needsOutjection()) {
176 List<BijectedAttribute<Out>> li = getComponent().getOutAttributes();
177 for (BijectedAttribute<Out> att: li) {
178 ScopeType scope = att.getAnnotation().scope();
179 if (ScopeType.UNSPECIFIED.equals(scope)) {
180 Component outComponent = Component.forName(att.getName());
181 if (outComponent != null)
182 scope = outComponent.getScope();
183 else
184 scope = getComponent().getScope();
185 }
186 if (ScopeType.STATELESS.equals(scope))
187 scope = ScopeType.EVENT;
188
189 if (!(ScopeType.EVENT.equals(scope))) {
190 Context context = Contexts.getEventContext();
191 if (context.get(att.getName() + "_tide_unspecified_") != null) {
192 context.remove(att.getName() + "_tide_unspecified_");
193 context.remove(att.getName());
194 }
195 }
196
197 tideContext.addResultEval(new ScopedContextResult(att.getName(), null, scope, restrict));
198 }
199
200 Field field = getComponent().getClass().getDeclaredField("dataModelGetters");
201 field.setAccessible(true);
202 List<BijectedAttribute<?>> dataModelGetters = (List<BijectedAttribute<?>>)field.get(getComponent());
203 for (BijectedAttribute<?> getter : dataModelGetters) {
204 Annotation anno = getter.getAnnotation();
205 DataBinder wrapper = anno.annotationType().getAnnotation(DataBinderClass.class).value().newInstance();
206 ScopeType scope = wrapper.getVariableScope(anno);
207 if (ScopeType.UNSPECIFIED.equals(scope))
208 scope = getComponent().getScope();
209 if (ScopeType.STATELESS.equals(scope))
210 scope = ScopeType.EVENT;
211
212 if (!(ScopeType.EVENT.equals(scope))) {
213 Context context = Contexts.getEventContext();
214 if (context.get(getter.getName() + "_tide_unspecified_") != null) {
215 context.remove(getter.getName() + "_tide_unspecified_");
216 context.remove(getter.getName());
217 }
218 }
219
220 tideContext.addResultEval(new ScopedContextResult(getter.getName(), null, scope, restrict));
221 }
222 }
223
224 // Force evaluation of factory components dependent on the called component
225 Set<FactoryVariable> factoredVariables = TideInit.getFactoredVariables(getComponent());
226 if (factoredVariables != null) {
227 for (FactoryVariable variable : factoredVariables) {
228 ScopeType scope = variable.getScope();
229 if (ScopeType.UNSPECIFIED.equals(scope))
230 scope = getComponent().getScope();
231 if (ScopeType.STATELESS.equals(scope))
232 scope = ScopeType.EVENT;
233
234 tideContext.addResultEval(new ScopedContextResult(variable.getVariableName(), null, scope, restrict));
235 }
236 }
237
238
239 if (evaluate)
240 tideInvocation.evaluated(tideContext.evaluateResults(getComponent(), invocation.getTarget(), false));
241
242 return result;
243 }
244 finally {
245 reentrant = false;
246 }
247 }
248
249 /**
250 * Try to determine what the PersistenceContext is and create an appropriate
251 * lazy initializer for it.
252 * @param ctx the bean bieng accessed.
253 */
254 private void checkForPersistenceContexts(InvocationContext ctx) {
255 Object bean = ctx.getTarget();
256 TidePersistenceManager pm = null;
257
258 for ( BijectedAttribute<?> ba: getComponent().getPersistenceContextAttributes() ) {
259 Object object = ba.get(bean);
260 SeamInitializer.instance().setTidePersistenceManager(TidePersistenceFactory.createTidePersistence(getComponent(), object));
261 return;
262 }
263
264 if (getComponent().needsInjection()) {
265 List<BijectedAttribute<In>> li = getComponent().getInAttributes();
266
267 for (BijectedAttribute<In> att: li) {
268
269 try {
270 pm = TidePersistenceFactory.createTidePersistence(getComponent(), att);
271 } catch (RuntimeException ex) {
272 continue;
273 }
274
275 if (pm != null) {
276 SeamInitializer.instance().setTidePersistenceManager(pm);
277 return;
278 }
279 }
280 }
281
282 //Last chance to see a PersistenceManager can be found for this invocation
283 pm = TidePersistenceFactory.createTidePersistence(getComponent(), ctx.getTarget());
284 if (pm != null)
285 SeamInitializer.instance().setTidePersistenceManager(pm);
286 }
287
288 // Needed for Seam 2.1
289 public boolean isInterceptorEnabled() {
290 return true;
291 }
292 }