/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.el;
import static java.lang.String.format;
import static org.mule.runtime.api.el.ValidationResult.failure;
import static org.mule.runtime.api.el.ValidationResult.success;
import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage;
import static org.mule.runtime.api.metadata.DataType.STRING;
import static org.mule.runtime.core.api.config.MuleProperties.OBJECT_EXPRESSION_LANGUAGE;
import static org.mule.runtime.core.util.ClassUtils.isInstance;
import static org.slf4j.LoggerFactory.getLogger;
import org.mule.runtime.api.el.BindingContext;
import org.mule.runtime.api.el.DefaultValidationResult;
import org.mule.runtime.api.el.ValidationResult;
import org.mule.runtime.api.lifecycle.Initialisable;
import org.mule.runtime.api.lifecycle.InitialisationException;
import org.mule.runtime.api.message.Message;
import org.mule.runtime.api.metadata.DataType;
import org.mule.runtime.api.metadata.TypedValue;
import org.mule.runtime.api.streaming.CursorProvider;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.Event.Builder;
import org.mule.runtime.core.api.MuleContext;
import org.mule.runtime.core.api.construct.FlowConstruct;
import org.mule.runtime.core.api.el.ExtendedExpressionLanguageAdaptor;
import org.mule.runtime.core.api.el.ExtendedExpressionManager;
import org.mule.runtime.core.api.el.GlobalBindingContextProvider;
import org.mule.runtime.core.api.expression.ExpressionRuntimeException;
import org.mule.runtime.core.api.transformer.TransformerException;
import org.mule.runtime.core.el.mvel.MVELExpressionLanguage;
import org.mule.runtime.core.streaming.StreamingManager;
import org.mule.runtime.core.util.OneTimeWarning;
import org.mule.runtime.core.util.TemplateParser;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import org.slf4j.Logger;
public class DefaultExpressionManager implements ExtendedExpressionManager, Initialisable {
public static final String DW_PREFIX = "dw";
public static final String MEL_PREFIX = "mel";
public static final String PREFIX_EXPR_SEPARATOR = ":";
private static final Logger LOGGER = getLogger(DefaultExpressionManager.class);
private final OneTimeWarning parseWarning = new OneTimeWarning(LOGGER,
"Expression parsing is deprecated, regular evaluations should be used instead.");
private final MuleContext muleContext;
private final StreamingManager streamingManager;
private final ExtendedExpressionLanguageAdaptor expressionLanguage;
// Default style parser
private final TemplateParser parser = TemplateParser.createMuleStyleParser();
private final boolean melDefault;
@Inject
public DefaultExpressionManager(MuleContext muleContext, StreamingManager streamingManager) {
this.muleContext = muleContext;
this.streamingManager = streamingManager;
final DataWeaveExpressionLanguageAdaptor dwExpressionLanguage = new DataWeaveExpressionLanguageAdaptor(muleContext);
final MVELExpressionLanguage mvelExpressionLanguage = muleContext.getRegistry().lookupObject(OBJECT_EXPRESSION_LANGUAGE);
this.expressionLanguage = new ExpressionLanguageAdaptorHandler(dwExpressionLanguage, mvelExpressionLanguage);
this.melDefault = ((ExpressionLanguageAdaptorHandler) expressionLanguage).isMelDefault();
}
@Override
public void initialise() throws InitialisationException {
Collection<GlobalBindingContextProvider> contextProviders =
muleContext.getRegistry().lookupObjects(GlobalBindingContextProvider.class);
for (GlobalBindingContextProvider contextProvider : contextProviders) {
expressionLanguage.addGlobalBindings(contextProvider.getBindingContext());
}
if (melDefault) {
LOGGER.warn("Using MEL as the default expression language.");
}
}
@Override
public void addGlobalBindings(BindingContext bindingContext) {
expressionLanguage.addGlobalBindings(bindingContext);
}
@Override
public TypedValue evaluate(String expression) {
return evaluate(expression, BindingContext.builder().build());
}
@Override
public TypedValue evaluate(String expression, Event event) {
return evaluate(expression, event, BindingContext.builder().build());
}
@Override
public TypedValue evaluate(String expression, BindingContext context) {
return evaluate(expression, null, null, null, context);
}
@Override
public TypedValue evaluate(String expression, Event event, BindingContext context) {
return evaluate(expression, event, Event.builder(event), null, context);
}
@Override
public TypedValue evaluate(String expression, Event event, FlowConstruct flowConstruct) {
return evaluate(expression, event, Event.builder(event), flowConstruct, BindingContext.builder().build());
}
@Override
public TypedValue evaluate(String expression, Event event, Event.Builder eventBuilder, FlowConstruct flowConstruct) {
return evaluate(expression, event, eventBuilder, flowConstruct, BindingContext.builder().build());
}
@Override
public TypedValue evaluate(String expression, Event event, FlowConstruct flowConstruct, BindingContext context) {
return evaluate(expression, event, Event.builder(event), flowConstruct, context);
}
private TypedValue evaluate(String expression, Event event, Event.Builder eventBuilder, FlowConstruct flowConstruct,
BindingContext context) {
return handleStreaming(expressionLanguage.evaluate(expression, event, eventBuilder, flowConstruct, context), event);
}
@Override
public TypedValue evaluate(String expression, DataType outputType) {
return evaluate(expression, outputType, BindingContext.builder().build());
}
@Override
public TypedValue evaluate(String expression, DataType outputType, BindingContext context) {
return evaluate(expression, outputType, context, null);
}
@Override
public TypedValue evaluate(String expression, DataType outputType, BindingContext context, Event event) {
return evaluate(expression, outputType, context, event, null, false);
}
@Override
public TypedValue evaluate(String expression, DataType outputType, BindingContext context, Event event,
FlowConstruct flowConstruct, boolean failOnNull)
throws ExpressionRuntimeException {
return handleStreaming(expressionLanguage.evaluate(expression, outputType, event, flowConstruct, context, failOnNull), event);
}
private TypedValue handleStreaming(TypedValue value, Event event) {
//TODO required a better fix for MULE-12486
if (event == null) {
return value;
}
Object payload = value.getValue();
if (payload instanceof CursorProvider) {
value = new TypedValue<>(streamingManager.manage((CursorProvider) payload, event), value.getDataType());
}
return value;
}
private TypedValue transform(TypedValue target, DataType sourceType, DataType outputType) throws TransformerException {
if (target.getValue() != null && !isInstance(outputType.getType(), target.getValue())) {
Object result = muleContext.getRegistry().lookupTransformer(sourceType, outputType).transform(target.getValue());
return new TypedValue<>(result, outputType);
} else {
return target;
}
}
@Override
public void enrich(String expression, Event event, Event.Builder eventBuilder, FlowConstruct flowConstruct, TypedValue value) {
expressionLanguage.enrich(expression, event, eventBuilder, flowConstruct, value);
}
@Override
public boolean evaluateBoolean(String expression, Event event, FlowConstruct flowConstruct)
throws ExpressionRuntimeException {
return evaluateBoolean(expression, event, flowConstruct, false, false);
}
@Override
public boolean evaluateBoolean(String expression, Event event, FlowConstruct flowConstruct, boolean nullReturnsTrue,
boolean nonBooleanReturnsTrue)
throws ExpressionRuntimeException {
return resolveBoolean(evaluate(expression, DataType.BOOLEAN, BindingContext.builder().build(), event, flowConstruct, false)
.getValue(), nullReturnsTrue,
nonBooleanReturnsTrue, expression);
}
protected boolean resolveBoolean(Object result, boolean nullReturnsTrue, boolean nonBooleanReturnsTrue, String expression) {
if (result == null) {
return nullReturnsTrue;
} else {
Object value = result;
if (value instanceof Boolean) {
return (Boolean) value;
} else if (value instanceof String) {
if (value.toString().toLowerCase().equalsIgnoreCase("false")) {
return false;
} else if (result.toString().toLowerCase().equalsIgnoreCase("true")) {
return true;
} else {
return nonBooleanReturnsTrue;
}
} else {
LOGGER.warn("Expression: " + expression + ", returned an non-boolean result. Returning: " + nonBooleanReturnsTrue);
return nonBooleanReturnsTrue;
}
}
}
@Override
public String parse(String expression, Event event, FlowConstruct flowConstruct) throws ExpressionRuntimeException {
Builder eventBuilder = Event.builder(event);
parseWarning.warn();
if (hasMelExpression(expression) || melDefault) {
return parser.parse(token -> {
Object result = evaluate(token, event, eventBuilder, flowConstruct).getValue();
if (result instanceof Message) {
return ((Message) result).getPayload().getValue();
} else {
return result;
}
}, expression);
} else if (isExpression(expression)) {
TypedValue evaluation = evaluate(expression, event, eventBuilder, flowConstruct);
try {
return (String) transform(evaluation, evaluation.getDataType(), STRING).getValue();
} catch (TransformerException e) {
throw new ExpressionRuntimeException(createStaticMessage(format("Failed to transform %s to %s.", evaluation.getDataType(),
STRING)),
e);
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("No expression marker found in expression '%s'. Parsing as plain String.", expression));
}
return expression;
}
}
private String parse(String expression, Event event, Event.Builder eventBuilder, FlowConstruct flowConstruct)
throws ExpressionRuntimeException {
parseWarning.warn();
if (hasMelExpression(expression) || melDefault) {
return parser.parse(token -> {
Object result = evaluate(token, event, eventBuilder, flowConstruct).getValue();
if (result instanceof Message) {
return ((Message) result).getPayload().getValue();
} else {
return result;
}
}, expression);
} else if (isExpression(expression)) {
TypedValue evaluation = evaluate(expression, event, eventBuilder, flowConstruct);
try {
return (String) transform(evaluation, evaluation.getDataType(), STRING).getValue();
} catch (TransformerException e) {
throw new ExpressionRuntimeException(createStaticMessage(format("Failed to transform %s to %s.", evaluation.getDataType(),
STRING)),
e);
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("No expression marker found in expression '%s'. Parsing as plain String.", expression));
}
return expression;
}
}
@Override
public Iterator<TypedValue<?>> split(String expression, int bachSize, Event event, FlowConstruct flowConstruct,
BindingContext bindingContext)
throws ExpressionRuntimeException {
return expressionLanguage.split(expression, bachSize, event, flowConstruct, bindingContext);
}
@Override
public Iterator<TypedValue<?>> split(String expression, int bachSize, Event event, BindingContext bindingContext)
throws ExpressionRuntimeException {
return expressionLanguage.split(expression, bachSize, event, bindingContext);
}
@Override
public boolean isExpression(String expression) {
return expression.contains(DEFAULT_EXPRESSION_PREFIX);
}
@Override
public boolean isValid(String expression) {
return validate(expression).isSuccess();
}
@Override
public ValidationResult validate(String expression) {
if (!muleContext.getConfiguration().isValidateExpressions()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Validate expressions is turned off, no checking done for: " + expression);
}
return new DefaultValidationResult(true, null);
}
final StringBuilder message = new StringBuilder();
try {
parser.validate(expression);
final AtomicBoolean valid = new AtomicBoolean(true);
if (expression.contains(DEFAULT_EXPRESSION_PREFIX)) {
parser.parse(token -> {
if (valid.get()) {
ValidationResult result = expressionLanguage.validate(token);
if (!result.isSuccess()) {
valid.compareAndSet(true, false);
message.append(token).append(" is invalid\n");
message.append(result.errorMessage().orElse(""));
}
}
return null;
}, expression);
} else {
return expressionLanguage.validate(expression);
}
} catch (IllegalArgumentException e) {
return failure(e.getMessage(), expression);
}
if (message.length() > 0) {
return failure(message.toString());
}
return success();
}
@Override
public Iterator<TypedValue<?>> split(String expression, int bachSize, BindingContext context) {
return expressionLanguage.split(expression, bachSize, null, context);
}
/**
* Checks if an expression has MEL prefix.
*
* @param expression the expression to check to see if is a MEL expression.
* @return true if the expression is a MEL expression
*/
public static boolean hasMelExpression(String expression) {
return expression.contains(DEFAULT_EXPRESSION_PREFIX + MEL_PREFIX + PREFIX_EXPR_SEPARATOR);
}
}