io.cloudslang.lang.runtime.bindings.scripts.ScriptEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.lang.runtime.bindings.scripts.ScriptEvaluator.java

Source

/*******************************************************************************
 * (c) Copyright 2016 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.lang.runtime.bindings.scripts;

import io.cloudslang.lang.entities.SystemProperty;
import io.cloudslang.lang.entities.bindings.ScriptFunction;
import io.cloudslang.lang.entities.bindings.values.PyObjectValue;
import io.cloudslang.lang.entities.bindings.values.Value;
import io.cloudslang.lang.entities.bindings.values.ValueFactory;
import io.cloudslang.runtime.api.python.PythonEvaluationResult;
import io.cloudslang.runtime.api.python.PythonRuntimeService;
import org.apache.commons.lang3.StringUtils;
import org.python.core.Py;
import org.python.core.PyObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author stoneo
 * @version $Id$
 * @since 06/11/2014
 */
@Component
public class ScriptEvaluator extends ScriptProcessor {
    private static String LINE_SEPARATOR = System.lineSeparator();
    private static final String SYSTEM_PROPERTIES_MAP = "sys_prop";
    private static final String GET_FUNCTION_DEFINITION = "def get(key, default_value=None):" + LINE_SEPARATOR
            + "  value = globals().get(key)" + LINE_SEPARATOR
            + "  return default_value if value is None else value";
    private static final String GET_SP_FUNCTION_DEFINITION = "def get_sp(key, default_value=None):" + LINE_SEPARATOR
            + "  property_value = " + SYSTEM_PROPERTIES_MAP + ".get(key)" + LINE_SEPARATOR
            + "  return default_value if property_value is None else property_value";
    private static final String CHECK_EMPTY_FUNCTION_DEFINITION = "def check_empty(value_to_check, default_value=None):"
            + LINE_SEPARATOR + "  return default_value if value_to_check is None else value_to_check";

    public static final int MAX_LENGTH = Integer.getInteger("input.error.max.length", 1000);

    @Autowired
    private PythonRuntimeService pythonRuntimeService;

    public Value evalExpr(String expr, Map<String, Value> context, Set<SystemProperty> systemProperties,
            Set<ScriptFunction> functionDependencies) {
        try {
            Map<String, Serializable> pythonContext = createPythonContext(context);
            boolean systemPropertiesDefined = functionDependencies.contains(ScriptFunction.GET_SYSTEM_PROPERTY);
            if (systemPropertiesDefined) {
                pythonContext.put(SYSTEM_PROPERTIES_MAP, (Serializable) prepareSystemProperties(systemProperties));
            }
            PythonEvaluationResult result = pythonRuntimeService.eval(buildAddFunctionsScript(functionDependencies),
                    expr, pythonContext);
            if (systemPropertiesDefined) {
                pythonContext.remove(SYSTEM_PROPERTIES_MAP);
            }

            return ValueFactory.create(result.getEvalResult(),
                    getSensitive(result.getResultContext(), systemPropertiesDefined));
        } catch (Exception exception) {
            throw new RuntimeException("Error in running script expression: '" + getTruncatedExpression(expr)
                    + "',\n\tException is: " + handleExceptionSpecialCases(exception.getMessage()), exception);
        }
    }

    private String getTruncatedExpression(String expr) {
        return expr.length() > MAX_LENGTH ? expr.substring(0, MAX_LENGTH) + "..." : expr;
    }

    private String buildAddFunctionsScript(Set<ScriptFunction> functionDependencies) {
        String functions = "";
        for (ScriptFunction function : functionDependencies) {
            switch (function) {
            case GET:
                functions += GET_FUNCTION_DEFINITION;
                functions = appendDelimiterBetweenFunctions(functions);
                break;
            case GET_SYSTEM_PROPERTY:
                functions += GET_SP_FUNCTION_DEFINITION;
                functions = appendDelimiterBetweenFunctions(functions);
                break;
            case CHECK_EMPTY:
                functions += CHECK_EMPTY_FUNCTION_DEFINITION;
                functions = appendDelimiterBetweenFunctions(functions);
                break;
            default:
                throw new RuntimeException(
                        "Error adding function to context: '" + function.getValue() + "' is not valid.");
            }
        }
        return functions;
    }

    private String appendDelimiterBetweenFunctions(String text) {
        return text + LINE_SEPARATOR + LINE_SEPARATOR;
    }

    private Map<String, Value> prepareSystemProperties(Set<SystemProperty> properties) {
        Map<String, Value> processedSystemProperties = new HashMap<>();
        for (SystemProperty property : properties) {
            processedSystemProperties.put(property.getFullyQualifiedName(),
                    ValueFactory.createPyObjectValue(property.getValue()));
        }
        return processedSystemProperties;
    }

    private String handleExceptionSpecialCases(String message) {
        String processedMessage = message;
        if (StringUtils.isNotEmpty(message) && message.contains("get_sp") && message.contains("not defined")) {
            processedMessage = message + ". Make sure to use correct syntax for the function:"
                    + " get_sp('fully.qualified.name', optional_default_value).";
        }
        return processedMessage;
    }

    private boolean getSensitive(Map<String, Serializable> executionResultContext,
            boolean systemPropertiesInContext) {
        if (systemPropertiesInContext) {
            Map<String, Serializable> context = new HashMap<>(executionResultContext);
            PyObject rawSystemProperties = (PyObject) context.remove(SYSTEM_PROPERTIES_MAP);
            @SuppressWarnings("unchecked")
            Map<String, Value> systemProperties = Py.tojava(rawSystemProperties, Map.class);
            @SuppressWarnings("unchecked")
            Collection<Serializable> systemPropertyValues = (Collection) systemProperties.values();
            return checkSensitivity(systemPropertyValues) || checkSensitivity(context.values());
        } else {
            return (checkSensitivity(executionResultContext.values()));
        }
    }

    private boolean checkSensitivity(Collection<Serializable> values) {
        for (Serializable value : values) {
            if (value != null && value instanceof PyObjectValue) {
                PyObjectValue pyObjectValue = (PyObjectValue) value;
                if (pyObjectValue.isSensitive() && pyObjectValue.isAccessed()) {
                    return true;
                }
            }
        }
        return false;
    }
}