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