All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.granite.tide.seam.TideInit Maven / Gradle / Ivy

The newest version!
/**
 *   GRANITE DATA SERVICES
 *   Copyright (C) 2006-2014 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 .
 */
package org.granite.tide.seam;

import static org.jboss.seam.annotations.Install.FRAMEWORK;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.granite.tide.annotations.BypassTideInterceptor;
import org.granite.tide.async.AsyncPublisher;
import org.granite.tide.data.DataEnabled;
import org.granite.tide.seam.async.TideAsynchronousInterceptor;
import org.jboss.seam.Component;
import org.jboss.seam.Namespace;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.annotations.intercept.InterceptorType;
import org.jboss.seam.async.AsynchronousInterceptor;
import org.jboss.seam.contexts.Context;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Init;
import org.jboss.seam.core.Init.FactoryExpression;
import org.jboss.seam.core.Init.FactoryMethod;
import org.jboss.seam.init.Initialization;
import org.jboss.seam.intercept.Interceptor;
import org.jboss.seam.log.Log;
import org.jboss.seam.util.Reflections;


/**
 * Particular initializer for Tide which instruments all Seam components
 * - Tide bijection/invocation interceptor is added
 * - Tide asynchronous interceptor is added
 * 
 * @author William DRAI
 */
@Scope(ScopeType.APPLICATION)
@Name("org.granite.tide.seam.init")
@Install(precedence=FRAMEWORK)
@Startup
@BypassInterceptors
public class TideInit {
	
	@Logger
	private Log log;
	
	private Map> factoryComponents = new HashMap>();
	
    
    public TideInit() {
        Context context = Contexts.getApplicationContext();
        for (String name : context.getNames()) {
            if (!name.endsWith(Initialization.COMPONENT_SUFFIX))
                continue;
            Object object = context.get(name);
            if (object instanceof Component) {
                Component component = (Component) object;
                if (component.isInterceptionEnabled() && component.getScope() != ScopeType.APPLICATION)
                    instrumentComponent(component);
            }
        }
        
        Init.instance().importNamespace("org.granite.tide");
        
        scanFactoryComponents();
    }
    
    
    public static Set getFactoredVariables(Component component) {
        TideInit init = (TideInit)Contexts.getApplicationContext().get(TideInit.class);    	
    	return init.factoryComponents.get(component);
    }
    
    
    @SuppressWarnings("unchecked")
    private void scanFactoryComponents() {
        try {
        	Init init = Init.instance();
        	
        	Field field = init.getClass().getDeclaredField("factories");
        	field.setAccessible(true);
        	Map factories = (Map)field.get(init);        	
        	for (Map.Entry factory : factories.entrySet()) {
        		String componentName = factory.getValue().getComponent().getName();
        		addFactoryVariable(componentName, factory.getKey(), factory.getValue().getScope());
        	}
        	
        	field = init.getClass().getDeclaredField("factoryMethodExpressions");
        	field.setAccessible(true);
        	Map factoryExpressions = (Map)field.get(init);        	
        	for (Map.Entry factoryExpression : factoryExpressions.entrySet()) {
        		String expressionString = factoryExpression.getValue().getMethodBinding().getExpressionString();
        		String componentName = resolveComponentName(expressionString);
        		if (componentName != null)
        			addFactoryVariable(componentName, factoryExpression.getKey(), factoryExpression.getValue().getScope());
        	}
        	
        	field = init.getClass().getDeclaredField("factoryValueExpressions");
        	field.setAccessible(true);
        	factoryExpressions = (Map)field.get(init);        	
        	for (Map.Entry factoryExpression : factoryExpressions.entrySet()) {
        		String expressionString = factoryExpression.getValue().getMethodBinding().getExpressionString();
        		String componentName = resolveComponentName(expressionString);
        		if (componentName != null)
        			addFactoryVariable(componentName, factoryExpression.getKey(), factoryExpression.getValue().getScope());
        	}
        }
        catch (Exception e) {
        	log.error("Could not initialize factory components map correctly", e);
        }
    }
    
    
    private String resolveComponentName(String expressionString) {
        Init init = Init.instance();
        
    	String expr = expressionString.startsWith("#{") ? expressionString.substring(2, expressionString.length()-1) : expressionString;
		int idx = expr.indexOf('.');
		String componentName = idx >= 0 ? expr.substring(0, idx) : expr;
		Component component = lookupComponent(componentName);
		if (component != null)
			return componentName;
    	
		if (idx < 0)
			return null;
		
    	Namespace ns = init.getRootNamespace().getChild(componentName);    	
    	while (ns != null) {
    		expr = expr.substring(idx+1);
    		idx = expr.indexOf('.');
    		String name = idx >= 0 ? expr.substring(0, idx) : expr;
    		
    		if (ns.hasChild(name))
    			ns = ns.getChild(name);
    		else {
        		try {
        			// Must use this hack to get the right name as all methods are private in Seam Namespace object
        			Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
        			m.setAccessible(true);
        			componentName = (String)Reflections.invoke(m, ns, name);
        			component = Component.forName(componentName);
        			return component != null ? componentName : null;
        		}
        		catch (Exception e) {
        			// Ignore
        		}
    		}
    	}
    	
    	return null;
    }
    
    
    private void addFactoryVariable(String componentName, String variableName, ScopeType scope) {
    	Component component = lookupComponent(componentName);
		Set variables = factoryComponents.get(component);
		if (variables == null) {
			variables = new HashSet();
			factoryComponents.put(component, variables);
		}
		variables.add(new FactoryVariable(variableName, scope));
    }
    
    
    public static final class FactoryVariable {
    	private final String variableName;
    	private final ScopeType scope;
    	
    	public FactoryVariable(String variableName, ScopeType scope) {
    		this.variableName = variableName;
    		this.scope = scope;
    	}
    	
    	public String getVariableName() {
    		return variableName;
    	}
    	
    	public ScopeType getScope() {
    		return scope;
    	}
    	
    	@Override
    	public boolean equals(Object obj) {
    		if (!(obj instanceof FactoryVariable))
    			return false;
    		return ((FactoryVariable)obj).variableName.equals(variableName) && ((FactoryVariable)obj).scope.equals(scope);    
    	}
    	
    	@Override
    	public int hashCode() {
    		return (variableName + "@" + scope).hashCode();
    	}
    }
    
    
    /**
     * Implementation of component lookup for Seam
     * Uses a hack to handle imports and fully qualified names
     * 
     * @param componentName name of the component
     * @return Component object
     */
    static Component lookupComponent(String componentName) {
        Init init = Init.instance();
        
        Component component = Component.forName(componentName);
        if (component != null)
        	return component;
        
    	// Look for the component in the imported namespaces (always give precedence to org.granite.tide overriden components)
    	for (Namespace ns : init.getGlobalImports()) {
            try {
                // Must use this hack to get the right name as all methods are private in Seam Namespace object
                Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
                m.setAccessible(true);
                String nsName = (String)Reflections.invoke(m, ns, componentName);
                if (!nsName.startsWith("org.granite.tide"))
                	continue;
                component = Component.forName(nsName);
                if (component != null)
                	return component;
            }
            catch (Exception e) {
                // Ignore
            }
        }
        
    	for (Namespace ns : init.getGlobalImports()) {
    		try {
    			// Must use this hack to get the right name as all methods are private in Seam Namespace object
    			Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
    			m.setAccessible(true);
    			String nsName = (String)Reflections.invoke(m, ns, componentName);
    			component = Component.forName(nsName);
    			if (component != null)
    				return component;
    		}
    		catch (Exception e) {
    			// Ignore
    		}
    	}
    	return null;
    }
    
    
    /**
     * Implementation of factory lookup for Seam
     * Uses a hack to manage imports and fully qualified names
     * 
     * @param componentName name of the factored component
     * @return FactoryMethod object
     */
    static FactoryMethod lookupFactory(String componentName) {
        Init init = Init.instance();
        
        FactoryMethod factoryMethod = init.getFactory(componentName);
        if (factoryMethod != null)
        	return factoryMethod;
        
    	// Look for the factory in the imported namespaces (always give precedence to org.granite.tide overriden components)
    	for (Namespace ns : init.getGlobalImports()) {
    		try {
    			// Must use this hack to get the right name as all methods are private in Seam Namespace object
    			Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
    			m.setAccessible(true);
    			String nsName = (String)Reflections.invoke(m, ns, componentName);
    			if (!nsName.startsWith("org.granite.tide"))
    				continue;
    			factoryMethod = init.getFactory(nsName);
    			if (factoryMethod != null)
    				return factoryMethod;
    		}
    		catch (Exception e) {
    			// Ignore
    		}
    	}
    	
    	for (Namespace ns : init.getGlobalImports()) {
    		try {
    			// Must use this hack to get the right name as all methods are private in Seam Namespace object
    			Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
    			m.setAccessible(true);
    			String nsName = (String)Reflections.invoke(m, ns, componentName);
    			factoryMethod = init.getFactory(nsName);
    			if (factoryMethod != null)
    				return factoryMethod;
    		}
    		catch (Exception e) {
    			// Ignore
    		}
    	}
    	
    	return null;
    }
    
    /**
     * Implementation of factory expression lookup for Seam
     * Uses a hack to manage imports and fully qualified names
     * 
     * @param componentName name of the factored component
     * @return FactoryMethod object
     */
    static FactoryExpression lookupFactoryExpression(String componentName) {
        Init init = Init.instance();
        
        FactoryExpression factoryExpression = init.getFactoryMethodExpression(componentName);
        if (factoryExpression != null)
        	return factoryExpression;
        factoryExpression = init.getFactoryValueExpression(componentName);
        if (factoryExpression != null)
        	return factoryExpression;
        
    	// Look for the factory expression in the imported namespaces (always give precedence to org.granite.tide overriden components)
    	for (Namespace ns : init.getGlobalImports()) {
    		try {
    			// Must use this hack to get the right name as all methods are private in Seam Namespace object
    			Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
    			m.setAccessible(true);
    			String nsName = (String)Reflections.invoke(m, ns, componentName);
    			if (!nsName.startsWith("org.granite.tide"))
    				continue;
    			
    	        factoryExpression = init.getFactoryMethodExpression(nsName);
    	        if (factoryExpression != null)
    	        	return factoryExpression;
    	        factoryExpression = init.getFactoryValueExpression(nsName);
    	        if (factoryExpression != null)
    	        	return factoryExpression;
    		}
    		catch (Exception e) {
    			// Ignore
    		}
    	}
    	for (Namespace ns : init.getGlobalImports()) {
    		try {
    			// Must use this hack to get the right name as all methods are private in Seam Namespace object
    			Method m = ns.getClass().getDeclaredMethod("qualifyName", String.class);
    			m.setAccessible(true);
    			String nsName = (String)Reflections.invoke(m, ns, componentName);
    			
    	        factoryExpression = init.getFactoryMethodExpression(nsName);
    	        if (factoryExpression != null)
    	        	return factoryExpression;
    	        factoryExpression = init.getFactoryValueExpression(nsName);
    	        if (factoryExpression != null)
    	        	return factoryExpression;
    		}
    		catch (Exception e) {
    			// Ignore
    		}
    	}
    	
    	return null;
    }
    
    
    private void instrumentComponent(Component component) {
        if (component.getBeanClass().isAnnotationPresent(BypassTideInterceptor.class))
        	return;
        
        List li = component.getInterceptors(InterceptorType.ANY);
        
        boolean newSortServer = false, newSortClient = false;
        	
        boolean found = false;
        for (Interceptor i : li) {
            if (i.getUserInterceptorClass().equals(TideInterceptor.class)) {
                found = true;
                break;
            }
        }
        if (!found) {
            component.addInterceptor(new Interceptor(new TideInterceptor(), component));
            newSortServer = true;
        }
        
        if (component.beanClassHasAnnotation(DataEnabled.class) && component.getBeanClass().getAnnotation(DataEnabled.class).useInterceptor()) {
        	found = false;
	        for (Interceptor i : li) {
	            if (i.getUserInterceptorClass().equals(TideDataPublishingInterceptor.class)) {
	                found = true;
	                break;
	            }
	        }
	        if (!found) {
	            component.addInterceptor(new Interceptor(new TideDataPublishingInterceptor(), component));
	            newSortServer = true;
	        }
        }
 
        // Check if AsyncPublisher installed
        AsyncPublisher asyncPublisher = (AsyncPublisher)Component.getInstance("org.granite.tide.seam.async.publisher");
        if (asyncPublisher != null) {
            boolean async = false;
            li = component.getClientSideInterceptors();
            for (Iterator i = li.iterator(); i.hasNext(); ) {
                Interceptor in = i.next();
                if (in.getUserInterceptorClass().equals(AsynchronousInterceptor.class)) {
                    async = true;
                    break;
                }
            }
            if (async) {
                component.addInterceptor(new Interceptor(new TideAsynchronousInterceptor(), component));
                newSortClient = true;
            }
        }
        
        if (newSortServer || newSortClient) {
            // Force correct sorting of interceptors: hack because newSort is private
            try {
                Method m = component.getClass().getDeclaredMethod("newSort", List.class);
                m.setAccessible(true);
                if (newSortServer)
                    m.invoke(component, component.getInterceptors(InterceptorType.SERVER));
                if (newSortClient)
                    m.invoke(component, component.getInterceptors(InterceptorType.CLIENT));
            }
            catch (Exception e) {
                // Ignore all
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy