io.cloudslang.worker.execution.reflection.ReflectionAdapterImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.worker.execution.reflection.ReflectionAdapterImpl.java

Source

/*******************************************************************************
* (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License v2.0 which accompany this distribution.
*
* The Apache License is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
*******************************************************************************/

package io.cloudslang.worker.execution.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import io.cloudslang.score.lang.ExecutionRuntimeServices;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import io.cloudslang.score.api.ControlActionMetadata;
import io.cloudslang.score.api.execution.ExecutionParametersConsts;
import io.cloudslang.score.exceptions.FlowExecutionException;
import io.cloudslang.worker.execution.services.SessionDataHandler;

/**
 * @author kravtsov
 * @author Avi Moradi
 * @since 09/11/2011
 * @version $Id$
 */
public class ReflectionAdapterImpl implements ReflectionAdapter, ApplicationContextAware {

    private static final Logger logger = Logger.getLogger(ReflectionAdapterImpl.class);

    @Autowired
    private SessionDataHandler sessionDataHandler;
    private ApplicationContext applicationContext;
    private Map<String, Object> cacheBeans = new ConcurrentHashMap<>();
    private Map<String, Method> cacheMethods = new ConcurrentHashMap<>();
    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    private Map<String, String[]> cacheParamNames = new ConcurrentHashMap<>();

    @Override
    public Object executeControlAction(ControlActionMetadata actionMetadata, Map<String, ?> actionData) {
        Validate.notNull(actionMetadata, "Action metadata is null");
        if (logger.isDebugEnabled())
            logger.debug("Executing control action [" + actionMetadata.getClassName() + '.'
                    + actionMetadata.getMethodName() + ']');
        try {
            Object actionBean = getActionBean(actionMetadata);
            Method actionMethod = getActionMethod(actionMetadata);
            Object[] arguments = buildParametersArray(actionMethod, actionData);
            if (logger.isTraceEnabled())
                logger.trace("Invoking...");
            Object result = actionMethod.invoke(actionBean, arguments);
            clearStateAfterInvocation(actionData);
            if (logger.isDebugEnabled())
                logger.debug("Control action [" + actionMetadata.getClassName() + '.'
                        + actionMetadata.getMethodName() + "] done");
            return result;
        } catch (IllegalArgumentException ex) {
            String message = "Failed to run the action! Wrong arguments were passed to class: "
                    + actionMetadata.getClassName() + ", method: " + actionMetadata.getMethodName() + ", reason: "
                    + ex.getMessage();
            throw new FlowExecutionException(message, ex);
        } catch (InvocationTargetException ex) {
            String message = ex.getTargetException() == null ? ex.getMessage()
                    : ex.getTargetException().getMessage();
            logger.error(getExceptionMessage(actionMetadata) + ", reason: " + message, ex);
            throw new FlowExecutionException(message, ex);
        } catch (Exception ex) {
            throw new FlowExecutionException(getExceptionMessage(actionMetadata) + ", reason: " + ex.getMessage(),
                    ex);
        }
    }

    private void clearStateAfterInvocation(Map<String, ?> actionData) {
        sessionDataHandler.setSessionDataInactive(getExecutionIdFromActionData(actionData));
    }

    private Object getActionBean(ControlActionMetadata actionMetadata)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Object bean = cacheBeans.get(actionMetadata.getClassName());
        if (bean == null) {
            if (logger.isTraceEnabled())
                logger.trace(actionMetadata.getClassName() + " wasn't found in the beans cache");
            Class<?> actionClass = Class.forName(actionMetadata.getClassName());
            try {
                bean = applicationContext.getBean(actionClass);
            } catch (Exception ex) { // Not a spring bean
                if (logger.isTraceEnabled())
                    logger.trace(ex);
            }
            if (bean == null)
                bean = actionClass.newInstance();
            cacheBeans.put(actionMetadata.getClassName(), bean);
            if (logger.isTraceEnabled())
                logger.trace(actionMetadata.getClassName() + " placed in the beans cache");
        }
        return bean;
    }

    private Method getActionMethod(ControlActionMetadata actionMetadata) throws ClassNotFoundException {
        Method actionMethod = cacheMethods
                .get(actionMetadata.getClassName() + '.' + actionMetadata.getMethodName());
        if (actionMethod == null) {
            if (logger.isTraceEnabled())
                logger.trace(actionMetadata.getClassName() + '.' + actionMetadata.getMethodName()
                        + " wasn't found in the methods cache");
            for (Method method : Class.forName(actionMetadata.getClassName()).getMethods()) {
                if (method.getName().equals(actionMetadata.getMethodName())) {
                    actionMethod = method;
                    cacheMethods.put(actionMetadata.getClassName() + '.' + actionMetadata.getMethodName(), method);
                    break;
                }
            }
        } else if (logger.isTraceEnabled()) {
            logger.trace(actionMetadata.getClassName() + '.' + actionMetadata.getMethodName()
                    + " was found in the methods cache");
        }
        if (actionMethod == null) {
            String errMessage = "Method: " + actionMetadata.getMethodName() + " was not found in class:  "
                    + actionMetadata.getClassName();
            logger.error(errMessage);
            throw new FlowExecutionException(errMessage);
        }
        return actionMethod;
    }

    private Object[] buildParametersArray(Method actionMethod, Map<String, ?> actionData) {
        String actionFullName = actionMethod.getDeclaringClass().getName() + "." + actionMethod.getName();
        String[] paramNames = cacheParamNames.get(actionFullName);
        if (paramNames == null) {
            paramNames = parameterNameDiscoverer.getParameterNames(actionMethod);
            cacheParamNames.put(actionFullName, paramNames);
        }
        List<Object> args = new ArrayList<>(paramNames.length);
        for (String paramName : paramNames) {
            if (ExecutionParametersConsts.NON_SERIALIZABLE_EXECUTION_DATA.equals(paramName)) {
                Long executionId = getExecutionIdFromActionData(actionData);
                Map<String, Object> nonSerializableExecutionData = sessionDataHandler
                        .getNonSerializableExecutionData(executionId);
                args.add(nonSerializableExecutionData);
                // If the control action requires non-serializable session data, we add it to the arguments array
                // and set the session data as active, so that it won't be cleared
                sessionDataHandler.setSessionDataActive(executionId);
                continue;
            }
            Object param = actionData.get(paramName);
            args.add(param);
        }
        return args.toArray(new Object[args.size()]);
    }

    private static Long getExecutionIdFromActionData(Map<String, ?> actionData) {
        ExecutionRuntimeServices executionRuntimeServices = (ExecutionRuntimeServices) actionData
                .get(ExecutionParametersConsts.EXECUTION_RUNTIME_SERVICES);
        if (executionRuntimeServices != null)
            return executionRuntimeServices.getExecutionId();
        return null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private static String getExceptionMessage(ControlActionMetadata actionMetadata) {
        return "Failed to run the action! Class: " + actionMetadata.getClassName() + ", method: "
                + actionMetadata.getMethodName();
    }

}