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 021package org.granite.tide.seam; 022 023import java.lang.annotation.Annotation; 024import java.lang.reflect.Field; 025import java.util.ArrayList; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.granite.tide.TidePersistenceManager; 031import org.granite.tide.annotations.BypassTideInterceptor; 032import org.granite.tide.invocation.ContextUpdate; 033import org.granite.tide.seam.TideInit.FactoryVariable; 034import org.granite.tide.seam.lazy.SeamInitializer; 035import org.granite.tide.seam.lazy.TidePersistenceFactory; 036import org.jboss.seam.Component; 037import org.jboss.seam.ScopeType; 038import org.jboss.seam.Component.BijectedAttribute; 039import org.jboss.seam.annotations.DataBinderClass; 040import org.jboss.seam.annotations.In; 041import org.jboss.seam.annotations.Out; 042import org.jboss.seam.annotations.intercept.AroundInvoke; 043import org.jboss.seam.annotations.intercept.Interceptor; 044import org.jboss.seam.annotations.security.Restrict; 045import org.jboss.seam.bpm.BusinessProcessInterceptor; 046import org.jboss.seam.contexts.Context; 047import org.jboss.seam.contexts.Contexts; 048import org.jboss.seam.core.BijectionInterceptor; 049import org.jboss.seam.core.EventInterceptor; 050import org.jboss.seam.databinding.DataBinder; 051import org.jboss.seam.intercept.AbstractInterceptor; 052import org.jboss.seam.intercept.InvocationContext; 053import org.jboss.seam.log.LogProvider; 054import 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}) 072public 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}