/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.tide.seam; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Entity; import javax.servlet.http.HttpSession; import org.granite.config.ConvertersConfig; import org.granite.config.GraniteConfig; import org.granite.context.GraniteContext; import org.granite.messaging.amf.io.util.ClassGetter; import org.granite.messaging.service.ServiceException; import org.granite.messaging.service.ServiceInvocationContext; import org.granite.messaging.webapp.HttpGraniteContext; import org.granite.tide.IInvocationCall; import org.granite.tide.IInvocationResult; import org.granite.tide.TidePersistenceManager; import org.granite.tide.TideServiceContext; import org.granite.tide.TideStatusMessages; import org.granite.tide.annotations.BypassTideMerge; import org.granite.tide.async.AsyncPublisher; import org.granite.tide.data.DataContext; import org.granite.tide.data.DataMergeContext; import org.granite.tide.data.DataUpdatePostprocessor; import org.granite.tide.invocation.ContextEvent; import org.granite.tide.invocation.ContextResult; import org.granite.tide.invocation.ContextUpdate; import org.granite.tide.invocation.InvocationCall; import org.granite.tide.invocation.InvocationResult; import org.granite.tide.seam.async.AsyncContext; import org.granite.tide.seam.lazy.SeamInitializer; import org.jboss.seam.Component; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.Factory; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.Observer; import org.jboss.seam.annotations.datamodel.DataModelSelection; import org.jboss.seam.annotations.security.Restrict; import org.jboss.seam.contexts.Context; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.core.Conversation; import org.jboss.seam.core.Init.FactoryExpression; import org.jboss.seam.core.Init.FactoryMethod; import org.jboss.seam.framework.Home; import org.jboss.seam.log.Log; import org.jboss.seam.security.Identity; import org.jboss.seam.util.Reflections; /** * @author William DRAI */ public abstract class AbstractSeamServiceContext extends TideServiceContext { private static final long serialVersionUID = 1L; public static final String COMPONENT_NAME = "org.granite.tide.seam.serviceContext"; protected @Logger Log log; private UserEvents userEvents; private boolean isAsynchronousContext = true; private static final String RESULTS_EVAL_ATTRIBUTE = AbstractSeamServiceContext.class.getName() + "_resultsEval"; private static final String RESULTS_EVAL_UNSPECIFIED_ATTRIBUTE = AbstractSeamServiceContext.class.getName() + "_resultsEval_unspecified"; public AbstractSeamServiceContext() throws ServiceException { super(); } /** * Determines the current sessionId for web invocations */ @Override public void initCall() { super.initCall(); if (userEvents != null) return; if (getSessionId() != null) userEvents = TideUserEvents.instance().getUserEvents(getSessionId()); else { GraniteContext graniteContext = GraniteContext.getCurrentInstance(); if (graniteContext instanceof HttpGraniteContext) { HttpSession session = ((HttpGraniteContext)graniteContext).getSession(false); if (session != null) setSessionId(session.getId()); isAsynchronousContext = false; } } } /** * Initialize current sessionId and event listeners for this context * * @param sessionId current sessionId */ @Override public void setSessionId(String sessionId) { super.setSessionId(sessionId); userEvents = TideUserEvents.instance().getUserEvents(sessionId); } /** * Clear current session from user events registry */ @Destroy public void endSession() { if (!isAsynchronousContext && getSessionId() != null) TideUserEvents.instance().unregisterSession(getSessionId()); } private Map<ContextResult, Boolean> getResultsEval(ScopeType scopeType) { Context context = Contexts.getEventContext(); String att = RESULTS_EVAL_UNSPECIFIED_ATTRIBUTE; if (scopeType == ScopeType.STATELESS) att = RESULTS_EVAL_ATTRIBUTE; else if (scopeType != ScopeType.UNSPECIFIED) { context = scopeType.getContext(); att = RESULTS_EVAL_ATTRIBUTE; } @SuppressWarnings("unchecked") Map<ContextResult, Boolean> resultsEval = (Map<ContextResult, Boolean>)context.get(att); if (resultsEval == null) { resultsEval = new HashMap<ContextResult, Boolean>(); context.set(att, resultsEval); } return resultsEval; } /** * Constructs an asynchronous context object * @return current context */ public AsyncContext getAsyncContext() { List<ContextResult> resultsEval = new ArrayList<ContextResult>(); for (ScopeType evalScopeType : EVAL_SCOPE_TYPES) resultsEval.addAll(getResultsEval(evalScopeType).keySet()); return new AsyncContext(getSessionId(), resultsEval); } /** * Restores an asynchronous context * @param asyncContext saved context */ public void setAsyncContext(AsyncContext asyncContext) { Contexts.getSessionContext().set("org.jboss.seam.security.identity", asyncContext.getIdentity()); setSessionId(asyncContext.getSessionId()); for (ContextResult resultEval : asyncContext.getResults()) { if (resultEval instanceof ScopedContextResult) getResultsEval(((ScopedContextResult)resultEval).getScope()).put(resultEval, Boolean.FALSE); else getResultsEval(ScopeType.UNSPECIFIED).put(resultEval, Boolean.FALSE); } } /** * Retrieve current messages * * @return list of messages */ protected abstract TideStatusMessages getTideMessages(); protected abstract void initTideMessages(); protected abstract void clearTideMessages(); /** * Implementation of component lookup for Seam service * * @param componentName component name */ @Override public Object findComponent(String componentName, Class<?> componentClass, String methodName) { Component component = TideInit.lookupComponent(componentName); if (component == null) return null; return Component.getInstance(component.getName()); } /** * Implementation of component lookup for Seam service * * @param componentName component name */ @Override public Set<Class<?>> findComponentClasses(String componentName, Class<?> componentClass, String methodName) { Component component = TideInit.lookupComponent(componentName); if (component == null) return null; return componentClasses(component); } private Set<Class<?>> componentClasses(Object component) { if (component instanceof Component) { Set<Class<?>> classes = new HashSet<Class<?>>(); for (Class<?> i : ((Component)component).getBusinessInterfaces()) classes.add(i); classes.add(((Component)component).getBeanClass()); return classes; } Set<Class<?>> classes = new HashSet<Class<?>>(1); classes.add(component.getClass()); return classes; } @Observer("org.jboss.seam.beginConversation") public void observeBeginConversation() { Contexts.getEventContext().set("org.granite.tide.conversation.wasCreated", true); } /** * Add an event in the current context * * @param type event type * @param params event parameters */ public void raiseEvent(String type, Object... params) { // Add the event to the current invocation TideInvocation tideInvocation = TideInvocation.get(); if (tideInvocation == null) return; if (userEvents != null) { // Avoid stupid infinite loop when creating locale selector as the log needs the current locale... if (!type.endsWith("org.jboss.seam.international.localeSelector")) log.debug("Intercept event %s", type); String sessionId = getSessionId(); if (sessionId != null && userEvents.hasEventType(type)) tideInvocation.addEvent(new ContextEvent(type, params)); } else if (Contexts.getSessionContext().isSet("org.granite.seam.login")) { // Force send of all events raised during login tideInvocation.addEvent(new ContextEvent(type, params)); } } /** * Factory for Seam async publisher * * @return servlet context of the current application */ @Override protected AsyncPublisher getAsyncPublisher() { return (AsyncPublisher)Component.getInstance("org.granite.tide.seam.async.publisher"); } /** * Synchronizes server context with data provided by the client * * @param context invocation context * @param c client call * @param componentName name of the component which will be invoked */ @Override public void prepareCall(ServiceInvocationContext context, IInvocationCall c, String componentName, Class<?> componentClass) { InvocationCall call = (InvocationCall)c; List<String> listeners = call.getListeners(); List<ContextUpdate> updates = call.getUpdates(); Object[] results = call.getResults(); if (Contexts.isEventContextActive() && Contexts.isSessionContextActive() && (Contexts.getSessionContext().isSet("org.granite.tide.isFirstCall") || Contexts.getSessionContext().isSet("org.granite.seam.login"))) { // Login tried : force evaluation of existing session context for (Map.Entry<ContextResult, Boolean> me : getResultsEval(ScopeType.SESSION).entrySet()) { if (me.getKey().getExpression() == null && Contexts.getSessionContext().isSet(me.getKey().getComponentName())) me.setValue(Boolean.TRUE); } Contexts.getSessionContext().remove("org.granite.seam.login"); Contexts.getSessionContext().remove("org.granite.tide.isFirstCall"); // Force quiet login if (Identity.instance().isLoggedIn() && Contexts.isEventContextActive()) Contexts.getEventContext().set("org.jboss.seam.security.silentLogin", true); } if (Contexts.isEventContextActive() && Contexts.isConversationContextActive() && Contexts.getConversationContext().isSet("org.granite.tide.isFirstConversationCall")) { // Join conversation : force evaluation of existing conversation context for (Map.Entry<ContextResult, Boolean> me : getResultsEval(ScopeType.CONVERSATION).entrySet()) { if (me.getKey().getExpression() == null && Contexts.getConversationContext().isSet(me.getKey().getComponentName())) me.setValue(Boolean.TRUE); } Contexts.getConversationContext().remove("org.granite.tide.isFirstConversationCall"); } String sessionId = getSessionId(); if (sessionId != null && listeners != null) { // Registers new event listeners for (String listener : listeners) TideUserEvents.instance().registerEventType(sessionId, listener); if (userEvents == null) userEvents = TideUserEvents.instance().getUserEvents(getSessionId()); } if (results != null) { Map<ContextResult, Boolean> resultsEval = getResultsEval(ScopeType.UNSPECIFIED); for (Object result : results) { ContextResult cr = (ContextResult)result; resultsEval.put(new ScopedContextResult(cr.getComponentName(), cr.getExpression(), ScopeType.UNSPECIFIED, null), Boolean.TRUE); // if (!factories.containsKey(cr.getComponentName())) { // FactoryExpression expr = Init.instance().getFactoryValueExpression(cr.getComponentName()); // if (expr != null) { // String vexpr = expr.getValueBinding().getExpressionString(); // vexpr = vexpr.substring(2, vexpr.indexOf('.', 2)); // factories.put(cr.getComponentName(), vexpr); // // resultsEval.put(new ContextResult(cr.getComponentName(), null), Boolean.TRUE); // } // } } } boolean instrumented = false; Component component = null; if (componentName != null) { component = TideInit.lookupComponent(componentName); if (component.isInterceptionEnabled()) instrumented = true; // Forces evaluation of all results if they are related to the called component for (Map.Entry<ContextResult, Boolean> me : getResultsEval(component.getScope()).entrySet()) { if (me.getKey().getComponentName().equals(componentName)) // || componentName.equals(factories.get(me.getKey().getComponentName()))) me.setValue(Boolean.TRUE); } } initTideMessages(); SeamInitializer.instance().restoreLoadedEntities(); if (Conversation.instance().isLongRunning()) { // Merge call arguments with current conversation context Object[] args = context.getParameters(); if (args != null) { for (int i = 0; i < args.length; i++) { Object value = mergeExternal(args[i], null); args[i] = value; } } } // Initialize an empty data context DataContext.init(); DataUpdatePostprocessor dataUpdatePostprocessor = (DataUpdatePostprocessor)Component.getInstance("org.granite.tide.seam.data.dataUpdatePreprocessor", true); if (dataUpdatePostprocessor != null) DataContext.get().setDataUpdatePostprocessor(dataUpdatePostprocessor); // Initialize invocation context with received changes to apply to the server context and results to return TideInvocation tideInvocation = TideInvocation.init(); tideInvocation.update(updates); if (!instrumented) { // If no interception enabled, force the update of the context for the current component // In other cases it will be done by the interceptor restoreContext(updates, component, null); tideInvocation.updated(); } } /** * Builds the result object for the invocation * * @param context invocation context * @param result result of the method invocation * @param componentName name of the invoked component * @return result object */ @Override public IInvocationResult postCall(ServiceInvocationContext context, Object result, String componentName, Class<?> componentClass) { TideInvocation tideInvocation = TideInvocation.get(); List<ContextUpdate> results = null; int scope = 3; boolean restrict = false; Component component = null; if (componentName != null) { // Determines scope of component component = TideInit.lookupComponent(componentName); if (Contexts.isMethodContextActive() && Contexts.getMethodContext().isSet(component.getName())) scope = 3; else if (Contexts.isEventContextActive() && Contexts.getEventContext().isSet(component.getName())) scope = 3; else if (Contexts.isPageContextActive() && Contexts.getPageContext().isSet(component.getName())) scope = 3; else if (Contexts.isConversationContextActive() && Contexts.getConversationContext().isSet(component.getName())) scope = 2; restrict = component.beanClassHasAnnotation(Restrict.class); } if (!tideInvocation.isEvaluated()) { // Do evaluation now if the interceptor has not been called results = evaluateResults(null, null, componentName == null && !(context != null && context.getMethod() != null && "resyncContext".equals(context.getMethod().getName()))); } else results = tideInvocation.getResults(); // Retrieve data updates made during the call DataContext dataContext = DataContext.get(); Object[][] updates = dataContext != null ? dataContext.getUpdates() : null; // Build the invocation result InvocationResult ires = new InvocationResult(result, results); ires.setScope(scope); ires.setRestrict(restrict); if (component != null) { if (component.beanClassHasAnnotation(BypassTideMerge.class) || component.businessInterfaceHasAnnotation(BypassTideMerge.class)) ires.setMerge(false); else if (context != null) { try { Method m = component.getBeanClass().getMethod(context.getMethod().getName(), context.getMethod().getParameterTypes()); if (m.isAnnotationPresent(BypassTideMerge.class)) ires.setMerge(false); } catch (NoSuchMethodException e) { log.warn("Could not find bean method", e); } for (Class<?> beanInterface : component.getBusinessInterfaces()) { try { Method m = beanInterface.getMethod(context.getMethod().getName(), context.getMethod().getParameterTypes()); if (m.isAnnotationPresent(BypassTideMerge.class)) { ires.setMerge(false); break; } } catch (NoSuchMethodException e) { log.warn("Could not find bean method", e); } } } } if (Conversation.instance().isLongRunning()) { // Put results in merge context to keep data in extended persistence context between remote calls DataMergeContext.addResultEntity(result); for (ContextUpdate cu : results) DataMergeContext.addResultEntity(cu.getValue()); } ires.setUpdates(updates); // Adds events in result object ires.setEvents(tideInvocation.getEvents()); // Save current set of entities loaded in a conversation scoped component to handle case of extended PM SeamInitializer.instance().saveLoadedEntities(); // Adds context messages in result object TideStatusMessages statusMessages = getTideMessages(); ires.setMessages(statusMessages.getMessages()); ires.setKeyedMessages(statusMessages.getKeyedMessages()); clearTideMessages(); // Clean thread TideInvocation.remove(); return ires; } /** * Intercepts a fault on the invocation * * @param context invocation context * @param t exception thrown * @param componentName name of the invoked component */ @Override public void postCallFault(ServiceInvocationContext context, Throwable t, String componentName, Class<?> componentClass) { clearTideMessages(); // Clean thread: very important to avoid phantom evaluations after exceptions TideInvocation.remove(); } public void addResultEval(ScopedContextResult result) { getResultsEval(result.getScope()).put(result, Boolean.TRUE); } /** * Evaluate updates in current server context * * @param updates list of updates * @param component the target component * @param target the target instance */ public void restoreContext(List<ContextUpdate> updates, Component component, Object target) { if (updates == null) return; GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig(); // Restore context for (ContextUpdate update : updates) { log.debug("Before invocation: evaluating expression #0.#1", update.getComponentName(), update.getExpression()); Component sourceComponent = TideInit.lookupComponent(update.getComponentName()); String sourceComponentName = sourceComponent != null ? sourceComponent.getName() : update.getComponentName(); Object previous = null; if (update.getExpression() != null) { // Set component property // Ignore expression on external components if target component is not interception enabled // to avoid issues with bijection, persistence contexts and transactions if (target != null || (component != null && component.getName().equals(sourceComponentName))) { // Get current values in Seam context String[] path = update.getExpression().split("\\."); Object instance = component != null && component.getName().equals(sourceComponentName) && target != null ? target : Component.getInstance(sourceComponentName); boolean disabled = instance != null && sourceComponent != null && config.isComponentTideDisabled(sourceComponentName, componentClasses(sourceComponent), instance); if (!disabled) { Object bean = instance; Object value = instance; List<Field> dmsFields = instance != null && path.length == 1 ? org.granite.util.Reflections.getFields(instance.getClass(), DataModelSelection.class) : null; List<String> dmsFieldNames = null; if (dmsFields != null && !dmsFields.isEmpty()) { dmsFieldNames = new ArrayList<String>(dmsFields.size()); for (Field f : dmsFields) dmsFieldNames.add(f.getName()); } if (update.getValue() != null) { boolean getPrevious = true; if (update.getValue().getClass().getAnnotation(Entity.class) != null) { org.granite.util.Entity entity = new org.granite.util.Entity(update.getValue()); if (entity.getIdentifier() == null) getPrevious = false; } if (getPrevious) { try { for (int i = 0; i < path.length; i++) { if (value == null) break; if (i == 0 && dmsFieldNames != null && dmsFieldNames.contains(path[0])) { Field field = org.granite.util.Reflections.getField(value.getClass(), path[i]); field.setAccessible(true); value = Reflections.get(field, value); if (i < path.length-1) bean = value; } else { // Use modified Reflections for getter because of a bug in Seam 2.0.0 Method getter = org.granite.util.Reflections.getGetterMethod(value.getClass(), path[i]); value = Reflections.invoke(getter, value); if (i < path.length-1) bean = value; } } } catch (IllegalArgumentException e) { // No getter found to retrieve current value log.warn("Partial merge only: " + e.getMessage()); value = null; } catch (Exception e) { throw new ServiceException("Could not get property: " + update.toString(), e); } previous = value; } } // Set new value try { if (bean != null && path.length == 1 && dmsFieldNames != null && dmsFieldNames.contains(path[0])) { Field field = org.granite.util.Reflections.getField(bean.getClass(), path[0]); field.setAccessible(true); value = ((ConvertersConfig)GraniteContext.getCurrentInstance().getGraniteConfig()).getConverters().convert(update.getValue(), field.getType()); // Merge entities into current persistent context if needed value = mergeExternal(value, previous); Reflections.set(field, bean, value); } else if (bean != null) { Method setter = Reflections.getSetterMethod(bean.getClass(), path[path.length-1]); Type type = setter.getParameterTypes()[0]; if (bean instanceof Home<?, ?> && "id".equals(path[path.length-1])) { // Special (ugly ?) handling for Home object to try to guess id type (setId is of type Object) try { Class<?> entityClass = ((Home<?, ?>)bean).getEntityClass(); org.granite.util.Entity entity = new org.granite.util.Entity(entityClass); type = entity.getIdentifierType(); } catch (Exception e) { // Ignore } } value = ((ConvertersConfig)GraniteContext.getCurrentInstance().getGraniteConfig()).getConverters().convert(update.getValue(), type); // Merge entities into current persistent context if needed value = mergeExternal(value, previous); Reflections.invoke(setter, bean, value); } } catch (Exception e) { throw new ServiceException("Could not restore property: " + update.toString(), e); } } } } else { // Set context variable if (sourceComponent != null) { ScopeType scope = sourceComponent.getScope(); if (!((update.getScope() == 2 && (scope == ScopeType.EVENT || scope == ScopeType.STATELESS || scope == ScopeType.CONVERSATION || scope == ScopeType.BUSINESS_PROCESS || scope == ScopeType.METHOD || scope == ScopeType.PAGE)) || (update.getScope() == 1 && (scope == ScopeType.EVENT || scope == ScopeType.STATELESS || scope == ScopeType.SESSION || scope == ScopeType.METHOD || scope == ScopeType.PAGE)) || (update.getScope() == 3 && (scope == ScopeType.EVENT || scope == ScopeType.STATELESS || scope == ScopeType.METHOD || scope == ScopeType.PAGE)))) { scope = ScopeType.EVENT; } previous = scope.getContext().get(sourceComponentName); boolean disabled = previous != null && config.isComponentTideDisabled(sourceComponentName, componentClasses(sourceComponent), previous); if (!disabled) { Object value = mergeExternal(update.getValue(), previous); scope.getContext().set(sourceComponentName, value); } } else { Object[] prev = lookupInStatefulContexts(sourceComponentName, ScopeType.UNSPECIFIED); ScopeType scope = ScopeType.UNSPECIFIED; if (prev != null) { previous = prev[0]; scope = (ScopeType)prev[1]; } boolean disabled = previous != null && config.isComponentTideDisabled(sourceComponentName, componentClasses(previous), previous); if (!disabled) { if (scope == ScopeType.UNSPECIFIED) { scope = ScopeType.EVENT; scope.getContext().set(sourceComponentName + "_tide_unspecified_", true); } Object value = mergeExternal(update.getValue(), previous); scope.getContext().set(sourceComponentName, value); } } } } } private static final ScopeType[] EVAL_SCOPE_TYPES = { ScopeType.UNSPECIFIED, ScopeType.EVENT, ScopeType.CONVERSATION, ScopeType.SESSION, ScopeType.BUSINESS_PROCESS, ScopeType.APPLICATION }; /** * Evaluate results from context * * @param component the target component * @param target the target instance * @param nothing used by initializer to avoid interactions with context sync * * @return list of updates to send back to the client */ public List<ContextUpdate> evaluateResults(Component component, Object target, boolean nothing) { List<ContextUpdate> resultsMap = new ArrayList<ContextUpdate>(); if (nothing) return resultsMap; List<String> exprs = new ArrayList<String>(); GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig(); ClassGetter classGetter = config.getClassGetter(); SeamInitializer.instance(); for (ScopeType evalScopeType : EVAL_SCOPE_TYPES) { for (Map.Entry<ContextResult, Boolean> me : getResultsEval(evalScopeType).entrySet()) { if (!me.getValue()) continue; ContextResult res = me.getKey(); Component targetComponent = TideInit.lookupComponent(res.getComponentName()); String targetComponentName = targetComponent != null ? targetComponent.getName() : res.getComponentName(); if (res.getExpression() != null && component != null && targetComponent != null && !(component.getName().equals(targetComponentName))) { // Ignore results concerning other components for this time // In this case is has to be a non interception enabled component continue; } int idx = 0; boolean add = true; // Force evaluation of consecutive properties while (idx >= 0) { idx = res.getExpression() != null ? res.getExpression().indexOf(".", idx+1) : -1; String expr = (idx > 0 ? res.getExpression().substring(0, idx) : res.getExpression()); String ex = expr != null ? res.getComponentName() + "." + expr : res.getComponentName(); if (!exprs.contains(ex)) { log.debug("After invocation: evaluating expression #0", ex); String[] path = expr != null ? expr.split("\\.") : new String[0]; try { Object value = null; ScopeType scopeType = res instanceof ScopedContextResult ? ((ScopedContextResult)res).getScope() : ScopeType.UNSPECIFIED; Boolean restrict = res.getRestrict(); FactoryMethod factoryMethod = null; FactoryExpression factoryExpression = null; if (targetComponent == null) { factoryExpression = TideInit.lookupFactoryExpression(res.getComponentName()); if (factoryExpression == null) factoryMethod = TideInit.lookupFactory(res.getComponentName()); } if (targetComponent != null) { if (component != null && component.getName().equals(targetComponent.getName())) { value = target; scopeType = targetComponent.getScope(); } else if (ScopeType.UNSPECIFIED.equals(scopeType)) { value = Component.getInstance(targetComponent.getName()); scopeType = targetComponent.getScope(); if (ScopeType.STATELESS.equals(scopeType)) scopeType = ScopeType.EVENT; if (value != null && config.isComponentTideDisabled(targetComponentName, componentClasses(targetComponent), value)) add = false; } else { value = Component.getInstance(targetComponent.getName(), scopeType); if (value != null && config.isComponentTideDisabled(targetComponentName, componentClasses(value), value)) add = false; } restrict = targetComponent.beanClassHasAnnotation(Restrict.class); } else if (factoryExpression != null) { String expressionString = factoryExpression.getMethodBinding() != null ? factoryExpression.getMethodBinding().getExpressionString() : factoryExpression.getValueBinding().getExpressionString(); int iedx = expressionString.indexOf("."); String expressionBaseName = expressionString.substring(2, iedx); if (ScopeType.UNSPECIFIED.equals(scopeType)) { value = Component.getInstance(res.getComponentName()); scopeType = factoryExpression.getScope(); if (ScopeType.STATELESS.equals(scopeType)) scopeType = ScopeType.EVENT; if (value != null && config.isComponentTideDisabled(expressionBaseName, componentClasses(value), value)) add = false; } else { value = Component.getInstance(res.getComponentName(), scopeType); if (value != null && config.isComponentTideDisabled(expressionBaseName, componentClasses(value), value)) add = false; } Component factoryComponent = TideInit.lookupComponent(expressionBaseName); restrict = factoryComponent != null ? factoryComponent.beanClassHasAnnotation(Restrict.class) : false; } else if (factoryMethod != null) { if (ScopeType.UNSPECIFIED.equals(scopeType)) { value = Component.getInstance(factoryMethod.getMethod().getAnnotation(Factory.class).value()); scopeType = factoryMethod.getScope(); if (ScopeType.STATELESS.equals(scopeType)) scopeType = ScopeType.EVENT; if (value != null && config.isComponentTideDisabled(factoryMethod.getComponent().getName(), componentClasses(factoryMethod.getComponent()), value)) add = false; } else { value = Component.getInstance(res.getComponentName(), scopeType); if (value != null && config.isComponentTideDisabled(factoryMethod.getComponent().getName(), componentClasses(value), value)) add = false; } restrict = factoryMethod.getComponent().beanClassHasAnnotation(Restrict.class); } else { Object[] val = lookupInStatefulContexts(res.getComponentName(), scopeType); if (val != null) { value = val[0]; scopeType = (ScopeType)val[1]; if (value != null && config.isComponentTideDisabled(res.getComponentName(), componentClasses(value), value)) add = false; } } if (add) { Object v0 = null; String propName = null; for (int i = 0; i < path.length; i++) { if (value == null) break; // Use modified Reflections for getter because of a bug in Seam 2.0.0 v0 = value; propName = path[i]; Method getter = null; try { getter = org.granite.util.Reflections.getGetterMethod(value.getClass(), path[i]); } catch (IllegalArgumentException e) { // GDS-566 } if (getter != null) value = Reflections.invoke(getter, value); } getResultsEval(scopeType).put(res, false); if (value instanceof TideDataModel) { // Unwrap value value = ((TideDataModel)value).getWrappedData(); } else if (value != null) { if (classGetter != null) { classGetter.initialize(v0, propName, value); if (res.getExpression() != null) { String[] fullPath = res.getExpression().split("\\."); Object v = value; for (int i = path.length; i < fullPath.length; i++) { // Use modified Reflections for getter because of a bug in Seam 2.0.0 Method getter = org.granite.util.Reflections.getGetterMethod(v.getClass(), fullPath[i]); v0 = v; v = Reflections.invoke(getter, v); // if (v == null) // break; classGetter.initialize(v0, fullPath[i], v); if (v == null) break; } } } } int scope = (scopeType == ScopeType.CONVERSATION ? 2 : (scopeType == ScopeType.SESSION ? 1 : 3)); resultsMap.add(new ContextUpdate(res.getComponentName(), expr, value, scope, Boolean.TRUE.equals(restrict))); add = false; } exprs.add(ex); } catch (Exception e) { throw new ServiceException("Could not evaluate result expression: " + ex, e); } } } me.setValue(Boolean.FALSE); } } return resultsMap; } /** * Implementations of intercepted asynchronous calls * Send asynchronous event to client * @param asyncContext current context (session id) * @param targetComponentName target component name * @param methodName method name * @param paramTypes method argument types * @param params argument values * @return result */ public Object invokeAsynchronous(AsyncContext asyncContext, String targetComponentName, Class<?> targetComponentClass, String methodName, Class<?>[] paramTypes, Object[] params) { setAsyncContext(asyncContext); // Just another ugly hack: the Seam interceptor has set this variable and we don't want it Contexts.getEventContext().remove("org.jboss.seam.async.AsynchronousIntercepter.REENTRANT"); Component component = TideInit.lookupComponent(targetComponentName); // Forces evaluation of all results if they are related to the called component for (Map.Entry<ContextResult, Boolean> me : getResultsEval(component.getScope()).entrySet()) { if (me.getKey().getComponentName().equals(targetComponentName)) me.setValue(Boolean.TRUE); } Object target = Component.getInstance(targetComponentName); Method method; try { method = target.getClass().getMethod(methodName, paramTypes); } catch (NoSuchMethodException nsme) { throw new IllegalStateException(nsme); } Object result = Reflections.invokeAndWrap(method, target, params); sendEvent(targetComponentName, targetComponentClass); return result; } @Override protected TidePersistenceManager getTidePersistenceManager(boolean create) { return SeamInitializer.instance().getTidePersistenceManager(); } /** * Search for a named attribute in all contexts, in the * following order: method, event, page, conversation, * session, business process, application. * * @return the first component found, or null */ public static Object[] lookupInStatefulContexts(String name, ScopeType scope) { if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.METHOD.equals(scope)) && Contexts.isMethodContextActive()) { Object result = Contexts.getMethodContext().get(name); if (result != null) return new Object[] { result, Contexts.getMethodContext().getType() }; } if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.EVENT.equals(scope)) && Contexts.isEventContextActive()) { Object result = Contexts.getEventContext().get(name); if (result != null) return new Object[] { result, Contexts.getEventContext().getType() }; } if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.PAGE.equals(scope)) && Contexts.isPageContextActive()) { Object result = Contexts.getPageContext().get(name); if (result != null) return new Object[] { result, Contexts.getPageContext().getType() }; } if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.CONVERSATION.equals(scope)) && Contexts.isConversationContextActive()) { Object result = Contexts.getConversationContext().get(name); if (result != null) return new Object[] { result, Contexts.getConversationContext().getType() }; } if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.SESSION.equals(scope)) && Contexts.isSessionContextActive()) { Object result = Contexts.getSessionContext().get(name); if (result != null) return new Object[] { result, Contexts.getSessionContext().getType() }; } if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.BUSINESS_PROCESS.equals(scope)) && Contexts.isBusinessProcessContextActive()) { Object result = Contexts.getBusinessProcessContext().get(name); if (result != null) return new Object[] { result, Contexts.getBusinessProcessContext().getType() }; } if ((ScopeType.UNSPECIFIED.equals(scope) || ScopeType.APPLICATION.equals(scope)) && Contexts.isApplicationContextActive()) { Object result = Contexts.getApplicationContext().get(name); if (result != null) return new Object[] { result, Contexts.getApplicationContext().getType() }; } return ScopeType.UNSPECIFIED.equals(scope) ? null : new Object[] { null, scope }; } }