/* * Copyright (c) 2010-2014 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; import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.expressions.SearchEvaluator; import com.evolveum.midpoint.model.impl.scripting.expressions.SelectEvaluator; import com.evolveum.midpoint.model.impl.scripting.helpers.JaxbHelper; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.marshaller.QueryConvertor; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.*; import com.evolveum.prism.xml.ns._public.types_3.RawType; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import java.util.HashMap; import java.util.Map; /** * Main entry point for evaluating scripting expressions. * * @author mederly */ @Component public class ScriptingExpressionEvaluator { private static final Trace LOGGER = TraceManager.getTrace(ScriptingExpressionEvaluator.class); private static final String DOT_CLASS = ScriptingExpressionEvaluator.class + "."; @Autowired private TaskManager taskManager; @Autowired private SearchEvaluator searchEvaluator; @Autowired private SelectEvaluator selectEvaluator; @Autowired private JaxbHelper jaxbHelper; @Autowired private PrismContext prismContext; private ObjectFactory objectFactory = new ObjectFactory(); private Map<String,ActionExecutor> actionExecutors = new HashMap<>(); /** * Asynchronously executes simple scripting expressions, consisting of one search command and one action. * * @param objectType Object type to search (e.g. c:UserType) * @param filter Filter to be applied (ObjectFilter) * @param actionName Action to be executed on objects found (e.g. "disable", "delete", "recompute", etc). * @param task Task in context of which the script should execute. The task should be "clean", i.e. * (1) transient, (2) without any handler. This method puts the task into background, * and assigns ScriptExecutionTaskHandler to it, to execute the script. * @param parentResult * @throws SchemaException */ @Deprecated public void evaluateExpressionInBackground(QName objectType, ObjectFilter filter, String actionName, Task task, OperationResult parentResult) throws SchemaException { Validate.notNull(objectType); Validate.notNull(actionName); Validate.notNull(task); SearchExpressionType search = new SearchExpressionType(); search.setType(objectType); if (filter != null) { search.setSearchFilter(QueryConvertor.createSearchFilterType(filter, prismContext)); } ActionExpressionType action = new ActionExpressionType(); action.setType(actionName); search.setScriptingExpression(objectFactory.createAction(action)); evaluateExpressionInBackground(search, task, parentResult); } /** * Asynchronously executes any scripting expression. * * @param expression Expression to be executed. * @param task Task in context of which the script should execute. The task should be "clean", i.e. * (1) transient, (2) without any handler. This method puts the task into background, * and assigns ScriptExecutionTaskHandler to it, to execute the script. * @param parentResult * @throws SchemaException */ public void evaluateExpressionInBackground(ScriptingExpressionType expression, Task task, OperationResult parentResult) throws SchemaException { OperationResult result = parentResult.createSubresult(DOT_CLASS + "evaluateExpressionInBackground"); if (!task.isTransient()) { throw new IllegalStateException("Task must be transient"); } if (task.getHandlerUri() != null) { throw new IllegalStateException("Task must not have a handler"); } ExecuteScriptType executeScriptType = new ExecuteScriptType(); executeScriptType.setScriptingExpression(jaxbHelper.toJaxbElement(expression)); task.setExtensionPropertyValue(SchemaConstants.SE_EXECUTE_SCRIPT, executeScriptType); task.setHandlerUri(ScriptExecutionTaskHandler.HANDLER_URI); taskManager.switchToBackground(task, result); result.computeStatus(); } /** * Entry point to _synchronous_ script execution, with no input data. * * @param expression Scripting expression to execute. * @param task Task in context of which the script should execute (in foreground!) * @param result Operation result * @return ExecutionContext, from which the caller can retrieve the output data via getFinalOutput() method, * and the console output via getConsoleOutput() method. * @throws com.evolveum.midpoint.model.api.ScriptExecutionException */ public ExecutionContext evaluateExpression(ScriptingExpressionType expression, Task task, OperationResult result) throws ScriptExecutionException { return evaluateExpression(expression, Data.createEmpty(), null, task, result); } public ExecutionContext evaluateExpression(@NotNull ExecuteScriptType executeScript, Task task, OperationResult result) throws ScriptExecutionException { // TODO parse input data Validate.notNull(executeScript.getScriptingExpression(), "Scripting expression must be present"); return evaluateExpression(executeScript.getScriptingExpression().getValue(), Data.createEmpty(), executeScript.getOptions(), task, result); } // use in tests only (we need the task at least to report progress/statistics) @Deprecated public ExecutionContext evaluateExpression(ScriptingExpressionType expression, OperationResult result) throws ScriptExecutionException { Task task = taskManager.createTaskInstance(); return evaluateExpression(expression, task, result); } // main entry point from the outside private ExecutionContext evaluateExpression(ScriptingExpressionType expression, Data data, ScriptingExpressionEvaluationOptionsType options, Task task, OperationResult result) throws ScriptExecutionException { ExecutionContext context = new ExecutionContext(options, task); Data output; try { output = evaluateExpression(expression, data, context, result); } catch (RuntimeException e) { result.recordFatalError("Couldn't execute script", e); throw new ScriptExecutionException("Couldn't execute script: " + e.getMessage(), e); } result.computeStatusIfUnknown(); context.setFinalOutput(output); return context; } public Data evaluateExpression(JAXBElement<? extends ScriptingExpressionType> expression, Data input, ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException { return evaluateExpression(expression.getValue(), input, context, parentResult); } public Data evaluateExpression(ScriptingExpressionType value, Data input, ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException { context.checkTaskStop(); OperationResult result = parentResult.createMinorSubresult(DOT_CLASS + "evaluateExpression"); Data output; if (value instanceof ExpressionPipelineType) { output = executePipeline((ExpressionPipelineType) value, input, context, result); } else if (value instanceof ExpressionSequenceType) { output = executeSequence((ExpressionSequenceType) value, input, context, result); } else if (value instanceof ForeachExpressionType) { output = executeForEach((ForeachExpressionType) value, input, context, result); } else if (value instanceof SelectExpressionType) { output = selectEvaluator.evaluate((SelectExpressionType) value, input, context, result); } else if (value instanceof FilterExpressionType) { output = executeFilter((FilterExpressionType) value, input, context, result); } else if (value instanceof SearchExpressionType) { output = searchEvaluator.evaluate((SearchExpressionType) value, input, context, result); } else if (value instanceof ActionExpressionType) { output = executeAction((ActionExpressionType) value, input, context, result); } else { throw new IllegalArgumentException("Unsupported expression type: " + (value==null?"(null)":value.getClass())); } result.computeStatusIfUnknown(); return output; } private Data executeAction(ActionExpressionType command, Data input, ExecutionContext context, OperationResult result) throws ScriptExecutionException { Validate.notNull(command, "command"); Validate.notNull(command.getType(), "command.actionType"); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Executing action {} on {}", command.getType(), input.debugDump()); } else if (LOGGER.isDebugEnabled()) { LOGGER.debug("Executing action {}", command.getType()); } ActionExecutor executor = actionExecutors.get(command.getType()); if (executor == null) { throw new IllegalStateException("Unsupported action type: " + command.getType()); } else { Data retval = executor.execute(command, input, context, result); result.setSummarizeSuccesses(true); result.summarize(); return retval; } } private Data executeFilter(FilterExpressionType command, Data input, ExecutionContext context, OperationResult result) { throw new NotImplementedException(); } private Data executeForEach(ForeachExpressionType command, Data input, ExecutionContext context, OperationResult result) { return null; } private Data executePipeline(ExpressionPipelineType pipeline, Data data, ExecutionContext context, OperationResult result) throws ScriptExecutionException { for (JAXBElement<? extends ScriptingExpressionType> expressionType : pipeline.getScriptingExpression()) { data = evaluateExpression(expressionType, data, context, result); } return data; } private Data executeSequence(ExpressionSequenceType sequence, Data input, ExecutionContext context, OperationResult result) throws ScriptExecutionException { Data lastOutput = null; for (JAXBElement<? extends ScriptingExpressionType> expressionType : sequence.getScriptingExpression()) { lastOutput = evaluateExpression(expressionType, input, context, result); } return lastOutput; } public Data evaluateConstantExpression(@NotNull RawType constant, @Nullable Class<?> expectedClass, ExecutionContext context, String desc, OperationResult result) throws ScriptExecutionException { try { // TODO fix this brutal hacking PrismValue value; if (expectedClass == null) { value = constant.getParsedValue(null, null); } else { Object object = constant.getParsedRealValue(expectedClass); if (object instanceof Referencable) { value = ((Referencable) object).asReferenceValue(); } else if (object instanceof Containerable) { value = ((Containerable) object).asPrismContainerValue(); } else { value = new PrismPropertyValue<>(object); } } if (value.isRaw()) { throw new IllegalStateException("Raw value while " + desc + ": " + value + ". Please specify type of the value."); } return Data.createItem(value, prismContext); } catch (SchemaException e) { throw new ScriptExecutionException(e.getMessage(), e); } } public Data evaluateConstantStringExpression(RawType constant, ExecutionContext context, OperationResult result) throws ScriptExecutionException { try { String value = constant.getParsedRealValue(String.class); return Data.createItem(new PrismPropertyValue<>(value), prismContext); } catch (SchemaException e) { throw new ScriptExecutionException(e.getMessage(), e); } } public void registerActionExecutor(String actionName, ActionExecutor executor) { actionExecutors.put(actionName, executor); } }