/*
* Copyright (c) 2010-2016 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.common.expression.script;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import com.evolveum.midpoint.model.common.expression.ExpressionVariables;
import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectResolver;
import com.evolveum.midpoint.schema.util.SchemaDebugUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.DebugUtil;
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.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionEvaluatorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionReturnTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
/**
* The expressions should be created by ExpressionFactory. They expect correct setting of
* expression evaluator and proper conversion form the XML ExpressionType. Factory does this.
*
* @author Radovan Semancik
*/
public class ScriptExpression {
private ScriptExpressionEvaluatorType scriptType;
private ScriptEvaluator evaluator;
private ItemDefinition outputDefinition;
private Function<Object, Object> additionalConvertor;
private ObjectResolver objectResolver;
private Collection<FunctionLibrary> functions;
private static final Trace LOGGER = TraceManager.getTrace(ScriptExpression.class);
private static final int MAX_CODE_CHARS = 42;
ScriptExpression(ScriptEvaluator evaluator, ScriptExpressionEvaluatorType scriptType) {
this.scriptType = scriptType;
this.evaluator = evaluator;
}
public ItemDefinition getOutputDefinition() {
return outputDefinition;
}
public void setOutputDefinition(ItemDefinition outputDefinition) {
this.outputDefinition = outputDefinition;
}
public ObjectResolver getObjectResolver() {
return objectResolver;
}
public void setObjectResolver(ObjectResolver objectResolver) {
this.objectResolver = objectResolver;
}
public Collection<FunctionLibrary> getFunctions() {
return functions;
}
public void setFunctions(Collection<FunctionLibrary> functions) {
this.functions = functions;
}
public Function<Object, Object> getAdditionalConvertor() {
return additionalConvertor;
}
public void setAdditionalConvertor(Function<Object, Object> additionalConvertor) {
this.additionalConvertor = additionalConvertor;
}
public <V extends PrismValue> List<V> evaluate(ExpressionVariables variables, ScriptExpressionReturnTypeType suggestedReturnType,
boolean useNew, String contextDescription, Task task, OperationResult result)
throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
ScriptExpressionEvaluationContext context = new ScriptExpressionEvaluationContext(variables, contextDescription, result, this);
context.setEvaluateNew(useNew);
try {
context.setupThreadLocal();
List<V> expressionResult = evaluator.evaluate(scriptType, variables, outputDefinition, additionalConvertor, suggestedReturnType, objectResolver, functions, contextDescription, task, result);
traceExpressionSuccess(variables, contextDescription, expressionResult);
return expressionResult;
} catch (ExpressionEvaluationException ex) {
traceExpressionFailure(variables, contextDescription, ex);
throw ex;
} catch (ObjectNotFoundException ex) {
traceExpressionFailure(variables, contextDescription, ex);
throw ex;
} catch (SchemaException ex) {
traceExpressionFailure(variables, contextDescription, ex);
throw ex;
} catch (RuntimeException ex) {
traceExpressionFailure(variables, contextDescription, ex);
throw ex;
} finally {
context.cleanupThreadLocal();
}
}
private void traceExpressionSuccess(ExpressionVariables variables, String shortDesc, Object returnValue) {
if (!isTrace()) {
return;
}
trace("Script expression trace:\n"+
"---[ SCRIPT expression {}]---------------------------\n"+
"Language: {}\n"+
"Relativity mode: {}\n"+
"Variables:\n{}\n"+
"Code:\n{}\n"+
"Result: {}", new Object[]{
shortDesc, evaluator.getLanguageName(), scriptType.getRelativityMode(), formatVariables(variables), formatCode(),
SchemaDebugUtil.prettyPrint(returnValue)
});
}
private void traceExpressionFailure(ExpressionVariables variables, String shortDesc, Exception exception) {
LOGGER.error("Expression error: {}", exception.getMessage(), exception);
if (!isTrace()) {
return;
}
trace("Script expression failure:\n"+
"---[ SCRIPT expression {}]---------------------------\n"+
"Language: {}\n"+
"Relativity mode: {}\n"+
"Variables:\n{}\n"+
"Code:\n{}\n"+
"Error: {}", new Object[]{
shortDesc, evaluator.getLanguageName(), scriptType.getRelativityMode(), formatVariables(variables), formatCode(),
SchemaDebugUtil.prettyPrint(exception)
});
}
private boolean isTrace() {
return LOGGER.isTraceEnabled() || (scriptType != null && scriptType.isTrace() == Boolean.TRUE);
}
private void trace(String msg, Object... args) {
if (scriptType != null && scriptType.isTrace() == Boolean.TRUE) {
LOGGER.info(msg, args);
} else {
LOGGER.trace(msg, args);
}
}
private String formatVariables(ExpressionVariables variables) {
if (variables == null) {
return "null";
}
return variables.formatVariables();
}
private String formatCode() {
return DebugUtil.excerpt(scriptType.getCode().replaceAll("[\\s\\r\\n]+", " "), MAX_CODE_CHARS);
}
public ItemPath parsePath(String path) {
if (path == null) {
return null;
}
ItemPathType itemPathType = new ItemPathType(path);
return itemPathType.getItemPath();
// TODO what about namespaces?
// Element codeElement = scriptType.getCode();
// XPathHolder xPathHolder = new XPathHolder(path, codeElement);
// if (xPathHolder == null) {
// return null;
// }
// return xPathHolder.toItemPath();
}
@Override
public String toString() {
return "ScriptExpression(" + formatCode() + ")";
}
}