com.evolveum.midpoint.model.impl.scripting.actions.ScriptExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.model.impl.scripting.actions.ScriptExecutor.java

Source

/*
 * Copyright (c) 2010-2017 Evolveum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.evolveum.midpoint.model.impl.scripting.actions;

import com.evolveum.midpoint.model.api.PipelineItem;
import com.evolveum.midpoint.model.api.ScriptExecutionException;
import com.evolveum.midpoint.model.common.expression.script.ScriptExpression;
import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory;
import com.evolveum.midpoint.model.impl.scripting.ExecutionContext;
import com.evolveum.midpoint.model.impl.scripting.PipelineData;
import com.evolveum.midpoint.model.impl.util.Utils;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.repo.common.expression.ExpressionFactory;
import com.evolveum.midpoint.repo.common.expression.ExpressionSyntaxException;
import com.evolveum.midpoint.repo.common.expression.ExpressionUtil;
import com.evolveum.midpoint.repo.common.expression.ExpressionVariables;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionEvaluatorType;
import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.xml.namespace.QName;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.evolveum.midpoint.model.impl.scripting.VariablesUtil.cloneIfNecessary;
import static com.evolveum.midpoint.schema.constants.SchemaConstants.NS_C;

/**
 * @author mederly
 */
@Component
public class ScriptExecutor extends BaseActionExecutor {

    //private static final Trace LOGGER = TraceManager.getTrace(ScriptExecutor.class);

    @Autowired
    private ScriptExpressionFactory scriptExpressionFactory;
    @Autowired
    private ExpressionFactory expressionFactory;

    private static final String NAME = "execute-script";
    private static final String PARAM_SCRIPT = "script";
    private static final String PARAM_OUTPUT_ITEM = "outputItem"; // item name or type (as URI!) -- EXPERIMENTAL
    private static final String PARAM_FOR_WHOLE_INPUT = "forWholeInput";

    @PostConstruct
    public void init() {
        scriptingExpressionEvaluator.registerActionExecutor(NAME, this);
    }

    @Override
    public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context,
            OperationResult globalResult) throws ScriptExecutionException {

        checkRootAuthorization(context, globalResult, NAME);

        ScriptExpressionEvaluatorType script = expressionHelper.getSingleArgumentValue(expression.getParameter(),
                PARAM_SCRIPT, true, true, NAME, input, context, ScriptExpressionEvaluatorType.class, globalResult);
        String outputItem = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_OUTPUT_ITEM,
                false, false, NAME, input, context, String.class, globalResult);
        boolean forWholeInput = expressionHelper.getArgumentAsBoolean(expression.getParameter(),
                PARAM_FOR_WHOLE_INPUT, input, context, false, PARAM_FOR_WHOLE_INPUT, globalResult);

        ItemDefinition<?> outputDefinition = getItemDefinition(outputItem);

        ScriptExpression scriptExpression;
        try {
            scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition,
                    expressionFactory, "script", context.getTask(), globalResult);
        } catch (ExpressionSyntaxException e) {
            throw new ScriptExecutionException("Couldn't parse script expression: " + e.getMessage(), e);
        }

        PipelineData output = PipelineData.createEmpty();

        if (forWholeInput) {
            OperationResult result = operationsHelper.createActionResult(null, this, context, globalResult);
            context.checkTaskStop();
            Throwable exception = null;
            try {
                Object outObject = executeScript(scriptExpression, input, context.getInitialVariables(), context,
                        result);
                if (outObject != null) {
                    addToData(outObject, PipelineData.newOperationResult(), output);
                } else {
                    // no definition means we don't plan to provide any output - so let's just copy the input item to the output
                    // (actually, null definition with non-null outObject should not occur)
                    if (outputDefinition == null) {
                        output.addAllFrom(input);
                    }
                }
            } catch (Throwable ex) {
                exception = processActionException(ex, NAME, null, context); // TODO value for error reporting (3rd parameter)
            }
            context.println((exception != null ? "Attempted to execute " : "Executed ") + "script on the pipeline"
                    + exceptionSuffix(exception));
            operationsHelper.trimAndCloneResult(result, globalResult, context);
        } else {
            for (PipelineItem item : input.getData()) {
                PrismValue value = item.getValue();
                OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult);

                context.checkTaskStop();
                String valueDescription;
                long started;
                if (value instanceof PrismObjectValue) {
                    started = operationsHelper.recordStart(context, asObjectType(value));
                    valueDescription = asObjectType(value).asPrismObject().toString();
                } else {
                    started = 0;
                    valueDescription = value.toHumanReadableString();
                }
                Throwable exception = null;
                try {
                    Object outObject = executeScript(scriptExpression, value, item.getVariables(), context, result);
                    if (outObject != null) {
                        addToData(outObject, item.getResult(), output);
                    } else {
                        // no definition means we don't plan to provide any output - so let's just copy the input item to the output
                        // (actually, null definition with non-null outObject should not occur)
                        if (outputDefinition == null) {
                            output.add(item);
                        }
                    }
                    if (value instanceof PrismObjectValue) {
                        operationsHelper.recordEnd(context, asObjectType(value), started, null);
                    }
                } catch (Throwable ex) {
                    if (value instanceof PrismObjectValue) {
                        operationsHelper.recordEnd(context, asObjectType(value), started, ex);
                    }
                    exception = processActionException(ex, NAME, value, context);
                }
                context.println((exception != null ? "Attempted to execute " : "Executed ") + "script on "
                        + valueDescription + exceptionSuffix(exception));
                operationsHelper.trimAndCloneResult(result, globalResult, context);
            }
        }
        return output;
    }

    private void addToData(@NotNull Object outObject, @NotNull OperationResult result, PipelineData output)
            throws SchemaException {
        if (outObject instanceof Collection) {
            for (Object o : (Collection) outObject) {
                addToData(o, result, output);
            }
        } else {
            PrismValue value;
            if (outObject instanceof PrismValue) {
                value = (PrismValue) outObject;
            } else if (outObject instanceof Objectable) {
                value = new PrismObjectValue<>((Objectable) outObject, prismContext);
            } else if (outObject instanceof Containerable) {
                value = new PrismContainerValue<>((Containerable) outObject, prismContext);
            } else {
                value = new PrismPropertyValue<>(outObject, prismContext);
            }
            output.add(new PipelineItem(value, result));
        }
    }

    private ItemDefinition<?> getItemDefinition(String itemUri) throws ScriptExecutionException {
        if (StringUtils.isBlank(itemUri)) {
            return null;
        }

        QName itemName = QNameUtil.uriToQName(itemUri, true);
        ItemDefinition def = prismContext.getSchemaRegistry().findItemDefinitionByElementName(itemName);
        if (def != null) {
            return def;
        }
        def = prismContext.getSchemaRegistry().findItemDefinitionByType(itemName);
        if (def != null) {
            return def;
        }
        throw new ScriptExecutionException(
                "Supplied item identification " + itemUri + " corresponds neither to item name nor type name");
    }

    private Object executeScript(ScriptExpression scriptExpression, Object input,
            Map<String, Object> externalVariables, ExecutionContext context, OperationResult result)
            throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
        ExpressionVariables variables = new ExpressionVariables();
        variables.addVariableDefinition(ExpressionConstants.VAR_INPUT, input);
        variables.addVariableDefinition(ExpressionConstants.VAR_PRISM_CONTEXT, prismContext);
        ExpressionUtil.addActorVariable(variables, securityContextManager);
        externalVariables
                .forEach((k, v) -> variables.addVariableDefinition(new QName(NS_C, k), cloneIfNecessary(k, v)));

        List<?> rv = Utils.evaluateScript(scriptExpression, null, variables, true, "in '" + NAME + "' action",
                context.getTask(), result);

        if (rv == null || rv.size() == 0) {
            return null;
        } else if (rv.size() == 1) {
            return rv.get(0);
        } else {
            return rv; // shouldn't occur; would cause problems
        }
    }

    private ObjectType asObjectType(PrismValue value) {
        return (ObjectType) ((PrismObjectValue) value).asObjectable();
    }

}