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

org.granite.tide.TideServiceInvoker Maven / Gradle / Ivy

There is a newer version: 3.0.0.M3
Show newest version
/*
  GRANITE DATA SERVICES
  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.

  This file is part of Granite Data Services.

  Granite Data Services is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library General Public License as published by
  the Free Software Foundation; either version 2 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 Library General Public License
  for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; if not, see .
*/

package org.granite.tide;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpSession;

import org.granite.config.flex.Destination;
import org.granite.context.GraniteContext;
import org.granite.logging.Logger;
import org.granite.messaging.amf.io.convert.Converters;
import org.granite.messaging.service.ServiceException;
import org.granite.messaging.service.ServiceFactory;
import org.granite.messaging.service.ServiceInvocationContext;
import org.granite.messaging.service.ServiceInvoker;
import org.granite.messaging.service.security.SecurityServiceException;
import org.granite.messaging.webapp.HttpGraniteContext;
import org.granite.tide.data.DataContext;
import org.granite.tide.data.DataEnabled;
import org.granite.tide.data.DataMergeContext;
import org.granite.tide.data.DataEnabled.PublishMode;
import org.granite.tide.validators.EntityValidator;
import org.granite.tide.validators.InvalidValue;
import org.granite.util.ClassUtil;

import flex.messaging.messages.RemotingMessage;

 
/**
 * Base class for Tide service invokers
 * Adapts the Tide invocation model with Granite
 * 
 * @author William DRAI
 */
public class TideServiceInvoker extends ServiceInvoker {
     
    private static final Logger log = Logger.getLogger(TideServiceInvoker.class);
    
    public static final String VALIDATOR_KEY = "org.granite.tide.validator.key";
    public static final String VALIDATOR_NOT_AVAILABLE = "org.granite.tide.validator.notAvailable";
    
    public static final String VALIDATOR_NAME = "validator-name";
    public static final String VALIDATOR_CLASS_NAME = "validator-class-name";
    
    /**
     * Current tide context
     */
    private TideServiceContext tideContext = null;
    
    private transient EntityValidator validator = null;
    
    
    public TideServiceInvoker(Destination destination, T factory) throws ServiceException {
        super(destination, factory);
        this.invokee = this;
        this.tideContext = lookupContext();
        this.tideContext.initCall();
        initValidator();
    }
    
    public TideServiceInvoker(Destination destination, T factory, TideServiceContext tideContext) throws ServiceException {
        super(destination, factory);
        this.invokee = this;
        this.tideContext = tideContext;
        this.tideContext.initCall();
        initValidator();
    }
    
    
    public Object initializeObject(Object parent, String[] propertyNames) {
        return tideContext.lazyInitialize(parent, propertyNames);
    }
    
    
    private static final InvalidValue[] EMPTY_INVALID_VALUES = new InvalidValue[0];
    
    protected void initValidator() {
    	Map applicationMap = GraniteContext.getCurrentInstance().getApplicationMap();
    	Boolean validatorNotAvailable = (Boolean)applicationMap.get(VALIDATOR_NOT_AVAILABLE);
    	validator = (EntityValidator)applicationMap.get(VALIDATOR_KEY);
    	
    	if (validator != null || Boolean.TRUE.equals(validatorNotAvailable))
    		return;
    	
        String className = this.destination.getProperties().get(VALIDATOR_CLASS_NAME);
        if (className != null) {
        	initValidatorWithClassName(className, null);
        	if (validator == null) {
        		log.warn("Validator class " + className + " not found: validation not enabled");
	            applicationMap.put(VALIDATOR_NOT_AVAILABLE, Boolean.TRUE);
        	}
        	else {
        		log.info("Validator class " + className + " initialized");
	        	applicationMap.put(VALIDATOR_KEY, validator);
        	}
        }
        else {
	    	String name = this.destination.getProperties().get(VALIDATOR_NAME);
	    	if (name != null) {
	    		try {
	    			validator = (EntityValidator)tideContext.findComponent(name, EntityValidator.class);
	    		}
	    		catch (ServiceException e) {
	    			name = null;
	    		}
	    	}
	    	
	    	if (validator == null) {
	    		className = "org.granite.tide.validation.BeanValidation";	    		
	    		initValidatorWithClassName(className, "javax.validation.ValidatorFactory");
	    	}
	    	
	    	if (validator == null) {
	    		if (name != null)
	    			log.warn("Validator component " + name + " not found: validation not enabled");
	    		else
	        		log.warn("Validator class " + className + " not found: validation not enabled");
	    		
    			applicationMap.put(VALIDATOR_NOT_AVAILABLE, Boolean.TRUE);
	    	}
        	else {
    			log.info("Validator class " + validator.getClass().getName() + " initialized");
	        	applicationMap.put(VALIDATOR_KEY, validator);
        	}
    	}
    }
    
    private void initValidatorWithClassName(String className, String constructorArgClassName) {
        try {
    		Object constructorArg = null;
    		Class constructorArgClass = null;
    		if (constructorArgClassName != null) {
	    		try {
	    			constructorArgClass = ClassUtil.forName(constructorArgClassName);
	    			constructorArg = tideContext.findComponent(null, constructorArgClass);
	    		}
	    		catch (Exception e) {
	    			// Constructor arg not found 
	    		}
    		}
    		
            Class validatorClass = Thread.currentThread().getContextClassLoader().loadClass(className);
            try {
            	Constructor c = validatorClass.getConstructor(constructorArgClass);
            	validator = (EntityValidator)c.newInstance(constructorArg);
            }
            catch (NoSuchMethodException e) {            	
            	validator = (EntityValidator)validatorClass.newInstance();
            }
            catch (InvocationTargetException e) {
            	log.error(e, "Could not initialize Tide validator " + className + " with argument of type " + constructorArgClassName);
            }
        }
        catch (ClassNotFoundException e) {
            // Ignore: Hibernate Validator not present
        }
        catch (NoClassDefFoundError e) {
            // Ignore: Hibernate Validator not present
        }
        catch (IllegalAccessException e) {
            // Ignore: Hibernate Validator not present
        }
        catch (InstantiationException e) {
            // Ignore: Hibernate Validator not present
        }
    }
    
    
    public InvalidValue[] validateObject(Object entity, String propertyName, Object value) {
    	initValidator();
        if (entity != null && validator != null)
            return validator.getPotentialInvalidValues(entity.getClass(), propertyName, value);
        
        return EMPTY_INVALID_VALUES;
    }

    
    public void login() {
    }
    
    public void logout() {
        HttpGraniteContext context = (HttpGraniteContext)GraniteContext.getCurrentInstance();
        HttpSession session = context.getSession(false);
        if (session != null)
            session.invalidate();
    }
    
    public void resyncContext() {
    }
    
    
    protected TideServiceContext lookupContext() {
        return null;
    }
    
    protected TideServiceContext getTideContext() {
        return tideContext;
    }
    
    
    
    @Override
    protected Object adjustInvokee(RemotingMessage request, String methodName, Object[] args) throws ServiceException {
        if ("invokeComponent".equals(methodName)) {
            String componentName = (String)args[0];
            String componentClassName = (String)args[1];
            Class componentClass = null;
            try {
	            if (componentClassName != null)
	            	componentClass = ClassUtil.forName(componentClassName);
            }
            catch (ClassNotFoundException e) {
            	throw new ServiceException("Component class not found " + componentClassName, e);
            }
            log.debug("Setting invokee to %s", componentName);
            
            Object instance = tideContext.findComponent(componentName, componentClass);
            Set> componentClasses = instance != null ? tideContext.findComponentClasses(componentName, componentClass) : null;
            
            GraniteContext context = GraniteContext.getCurrentInstance();
            if (instance != null && componentClasses != null && context.getGraniteConfig().isComponentTideEnabled(componentName, componentClasses, instance))
                return tideContext.adjustInvokee(instance, componentName, componentClasses);
            
            if (instance != null)
                log.error("SECURITY CHECK: Remote call refused to a non Tide-enabled component: " + componentName + "." + args[1] + ", class: " + componentClasses + ", instance: " + instance);
            throw SecurityServiceException.newAccessDeniedException("Component [" + componentName + (componentClassName != null ? " of class " + componentClassName : "") + "] not found");
        }
        
        return super.adjustInvokee(request, methodName, args);
    }
    

    @Override
    protected Object[] beforeMethodSearch(Object invokee, String methodName, Object[] args) {
        if ("invokeComponent".equals(methodName)) { 
        	return tideContext.beforeMethodSearch(invokee, methodName, args);
        } 
        else if ("initializeObject".equals(methodName)) {
        	return new Object[] { methodName, new Object[] { args[0], args[1] } };
        } 
        else if ("validateObject".equals(methodName)) {
            return new Object[] { methodName, new Object[] { args[0], args[1], args[2] } };
        }
        	
        return new Object[] { methodName, new Object[] {} };
    }

    
    private static final String DATAENABLED_HANDLED = "org.granite.tide.invoker.dataEnabled";

    @Override
    protected void beforeInvocation(ServiceInvocationContext context) {
        RemotingMessage message = (RemotingMessage)context.getMessage();
        GraniteContext graniteContext = GraniteContext.getCurrentInstance();
        
        Object[] originArgs = (Object[])message.getBody();
        IInvocationCall call = (IInvocationCall)originArgs[originArgs.length-1];
        
        String operation = message.getOperation();
        String componentName = "invokeComponent".equals(operation) ? (String)originArgs[0] : null;
        String componentClassName = "invokeComponent".equals(operation) ? (String)originArgs[1] : null;
        Class componentClass = null;
        try {
	        if (componentClassName != null)
	        	componentClass = ClassUtil.forName(componentClassName);
        }
        catch (ClassNotFoundException e) {
        	throw new ServiceException("Component class not found " + componentClassName, e);
        }
        
        graniteContext.getRequestMap().put(TideServiceInvoker.class.getName(), this);
        
        if (componentName != null || componentClass != null) {
	    	Converters converters = graniteContext.getGraniteConfig().getConverters();
	    	
	    	Set> componentClasses = tideContext.findComponentClasses(componentName, componentClass);
	    	for (Class cClass : componentClasses) {
	    		try {
	    			Method m = cClass.getMethod(context.getMethod().getName(), context.getMethod().getParameterTypes());
	    			for (int i = 0; i < m.getGenericParameterTypes().length; i++)
	    				context.getParameters()[i] = converters.convert(context.getParameters()[i], m.getGenericParameterTypes()[i]);
	    			
	    			break;
	    		}
	    		catch (NoSuchMethodException e) {
	    		}
	    	}
	    	
	    	for (Class cClass : componentClasses) {
	        	DataEnabled dataEnabled = cClass.getAnnotation(DataEnabled.class);
	        	if (dataEnabled != null && !dataEnabled.useInterceptor()) {
	        		GraniteContext.getCurrentInstance().getRequestMap().put(DATAENABLED_HANDLED, true);
	        		DataContext.init(dataEnabled.topic(), dataEnabled.params(), dataEnabled.publish());
	    			prepareDataObserver(dataEnabled);
	        		break;
	        	}
	    	}
        }
        
        Throwable error = null;
        try {
        	tideContext.prepareCall(context, call, componentName, componentClass);
        }
        catch (ServiceException e) {
        	error = e;
        }
        catch (Throwable e) {
        	if (e instanceof InvocationTargetException)
        		error = ((InvocationTargetException)e).getTargetException();
        	else
        		error = e;
        } 
        finally {
        	if (error != null)
        		throw factory.getServiceExceptionHandler().handleInvocationException(context, error);
        }
    }
        
    protected void prepareDataObserver(DataEnabled dataEnabled) {
		DataContext.observe();
    }


    @Override
    protected Object afterInvocation(ServiceInvocationContext context, Object result) {
    	Object res = null;
    	
    	String componentName = null;
        Class componentClass = null;
    	try {
	        Object[] originArgs = (Object[])context.getMessage().getBody();
	        String operation = ((RemotingMessage)context.getMessage()).getOperation();
	        componentName = "invokeComponent".equals(operation) ? (String)originArgs[0] : null;
	        String componentClassName = "invokeComponent".equals(operation) ? (String)originArgs[1] : null;
	        try {
		        if (componentClassName != null)
		        	componentClass = ClassUtil.forName(componentClassName);
	        }
	        catch (ClassNotFoundException e) {
	        	throw new ServiceException("Component class not found " + componentClassName, e);
	        }
    	}
    	finally {
            Throwable error = null;
            try {
        		res = tideContext.postCall(context, result, componentName, componentClass);
            }
            catch (ServiceException e) {
            	error = e;
            }
            catch (Throwable e) {
            	if (e instanceof InvocationTargetException)
            		error = ((InvocationTargetException)e).getTargetException();
            	else
            		error = e;
            }
            finally {
            	if (error != null)
            		throw factory.getServiceExceptionHandler().handleInvocationException(context, error);
            }
    	}
    	
    	DataMergeContext.remove();
    	
    	// DataContext has been setup by beforeInvocation
		if (GraniteContext.getCurrentInstance().getRequestMap().get(DATAENABLED_HANDLED) != null)
	    	publishDataUpdates();
    	
		DataContext.remove();
    	
    	return res;
    }
    
    protected void publishDataUpdates() {
		DataContext.publish(PublishMode.ON_SUCCESS);
    }

    
    @Override
    protected void afterInvocationError(ServiceInvocationContext context, Throwable error) {
    	String componentName = null;
        Class componentClass = null;
    	try {
	        Object[] originArgs = (Object[])context.getMessage().getBody();
	        String operation = ((RemotingMessage)context.getMessage()).getOperation();
	        componentName = "invokeComponent".equals(operation) ? (String)originArgs[0] : null;
	        String componentClassName = "invokeComponent".equals(operation) ? (String)originArgs[1] : null;
	        try {
		        if (componentClassName != null)
		        	componentClass = ClassUtil.forName(componentClassName);
	        }
	        catch (ClassNotFoundException e) {
	        	throw new ServiceException("Component class not found " + componentClassName, e);
	        }
    	}
    	finally {
    		tideContext.postCallFault(context, error, componentName, componentClass);
    	}
    	
    	DataMergeContext.remove();
		DataContext.remove();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy