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