/*******************************************************************************
* Copyright 2017 Graz University of Technology
* EAAF-Core Components has been developed in a cooperation between EGIZ,
* A-SIT Plus, A-SIT, and Graz University of Technology.
*
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/news/understanding-eupl-v12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*******************************************************************************/
/*******************************************************************************
*******************************************************************************/
/*******************************************************************************
*******************************************************************************/
package at.gv.egiz.eaaf.core.impl.idp.process.springweb;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext;
import at.gv.egiz.eaaf.core.api.idp.process.ExpressionEvaluationContext;
import at.gv.egiz.eaaf.core.api.idp.process.ExpressionEvaluator;
import at.gv.egiz.eaaf.core.impl.idp.process.model.Transition;
/**
* Expression evaluator for processing {@link Transition} conditions allowing to
*
* - reference Spring beans from the application context using {@code @myBeanName...},
* - {@link ExecutionContext} properties using {@code ctx['property']},
* - Multi valued {@link HttpServletRequest} parameters using {@code requestParameters['foo']} (keep in mind that this
* expression returns an array of String values) and
* - Single valued {@link HttpServletRequest} parameters using {@code requestParameter['foo']}
*
*
* @author tknall
*
*/
public class SpringWebExpressionEvaluator implements ExpressionEvaluator {
private Logger log = LoggerFactory.getLogger(getClass());
private ExpressionParser parser = new SpelExpressionParser();
private StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
@Autowired(required = false)
private ApplicationContext ctx;
@Autowired(required = false)
private HttpServletRequest request;
@PostConstruct
private void init() {
if (ctx != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(ctx));
}
}
/**
* Evaluation context that provides access to {@link HttpServletRequest} parameters using
* {@code requestParameter['foo']} for single value parameters or {@code requestParameters['foo']} for multi value
* parameters. Basic calls to {@code ctx} will be delegated.
*
* @author tknall
*
*/
private class SpringWebExpressionEvaluationContext implements ExpressionEvaluationContext {
private static final long serialVersionUID = 1L;
/**
* Creates a new expression evaluation context, providing access to HttpServletRequest parameter(s).
*
* @param delegate
* The original {@link ExpressionEvaluationContext} to be delegated to for {@code ctx['foo']}
* expressions.
*/
public SpringWebExpressionEvaluationContext(ExpressionEvaluationContext delegate) {
this.delegate = delegate;
}
private ExpressionEvaluationContext delegate;
@Override
public Map getCtx() {
return delegate.getCtx();
}
@SuppressWarnings("unused")
public Map getRequestParameter() {
if (request != null) {
Map singleValueMap = new HashMap();
Iterator> it = request.getParameterMap().entrySet().iterator();
while (it.hasNext()) {
Entry entry = it.next();
if (ArrayUtils.isNotEmpty(entry.getValue())) {
singleValueMap.put(entry.getKey(), entry.getValue()[0]);
}
}
return singleValueMap;
} else {
return Collections. emptyMap();
}
}
@SuppressWarnings("unused")
public Map getRequestParameters() {
if (request != null) {
return request.getParameterMap();
} else {
return Collections. emptyMap();
}
}
}
@Override
public boolean evaluate(ExpressionEvaluationContext expressionContext, String expression) {
Objects.requireNonNull(expression, "Expression must not be null.");
log.trace("Evaluating '{}'.", expression);
Expression expr = parser.parseExpression(expression);
Boolean result = null;
try {
result = expr.getValue(evaluationContext, new SpringWebExpressionEvaluationContext(expressionContext),
Boolean.class);
if (result == null) {
log.debug("Evaluation of '{}' results in null-value.", expression);
} else {
log.debug("Expression '{}' -> {}", expression, result);
}
} catch (Exception e) {
log.warn("Expression '{}' could not be processed.", expression, e);
}
return BooleanUtils.isTrue(result);
}
}