From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../springweb/AbstractAuthSourceServlet.java | 118 +++++++++++++++++ .../impl/idp/process/springweb/AbstractTask.java | 101 ++++++++++++++ .../springweb/SpringWebExpressionEvaluator.java | 145 +++++++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractAuthSourceServlet.java create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractTask.java create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/SpringWebExpressionEvaluator.java (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb') diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractAuthSourceServlet.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractAuthSourceServlet.java new file mode 100644 index 00000000..095c3439 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractAuthSourceServlet.java @@ -0,0 +1,118 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.process.springweb; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.api.idp.process.ProcessEngine; +import at.gv.egiz.eaaf.core.impl.idp.process.ProcessInstance; + +/** + * Abstract HttpServlet that provides means for retrieving the process engine (Spring Web required) as well as + * retrieving the underlying process instance and execution context evaluating a certain request parameter. + * + * @author tknall + * + */ +public abstract class AbstractAuthSourceServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + private ProcessEngine processEngine; + + /** + * Returns the name of the request parameter representing the respective instance id. + *

Default is {@code processInstanceId}. + * @return The request parameter name. + */ + public String getProcessInstanceIdParameterName() { + return "processInstanceId"; + } + + /** + * Returns the underlying process engine instance. + * + * @return The process engine (never {@code null}). + * @throws NoSuchBeanDefinitionException + * if no {@link ProcessEngine} bean was found. + * @throws NoUniqueBeanDefinitionException + * if more than one {@link ProcessEngine} bean was found. + * @throws BeansException + * if a problem getting the {@link ProcessEngine} bean occurred. + * @throws IllegalStateException + * if the Spring WebApplicationContext was not found, which means that the servlet is used outside a + * Spring web environment. + */ + public synchronized ProcessEngine getProcessEngine() { + if (processEngine == null) { + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); + if (ctx == null) { + throw new IllegalStateException( + "Unable to find Spring WebApplicationContext. Servlet needs to be executed within a Spring web environment."); + } + processEngine = ctx.getBean(ProcessEngine.class); + } + return processEngine; + } + + /** + * Retrieves the process instance referenced by the request parameter {@link #getProcessInstanceIdParameterName()}. + * + * @param request + * The HttpServletRequest. + * @return The process instance (never {@code null}). + * @throws NoSuchBeanDefinitionException + * if no {@link ProcessEngine} bean was found. + * @throws NoUniqueBeanDefinitionException + * if more than one {@link ProcessEngine} bean was found. + * @throws BeansException + * if a problem getting the {@link ProcessEngine} bean occurred. + * @throws IllegalStateException + * if the Spring WebApplicationContext was not found, which means that the servlet is used outside a + * Spring web environment. + * @throws IllegalArgumentException + * in case the process instance id referenced by the request parameter + * {@link #getProcessInstanceIdParameterName()} does not exist. + */ + public ProcessInstance getProcessInstance(HttpServletRequest request) { + String processInstanceId = StringUtils.trimToNull(request.getParameter(getProcessInstanceIdParameterName())); + if (processInstanceId == null) { + throw new IllegalArgumentException("Missing request parameter '" + getProcessInstanceIdParameterName() + "'."); + } + return getProcessEngine().getProcessInstance(processInstanceId); + } + + /** + * Retrieves the execution context for the respective process instance referenced by the request parameter + * {@link #getProcessInstanceIdParameterName()}. + * + * @param request + * The HttpServletRequest. + * @return The execution context (never {@code null}). + * @throws NoSuchBeanDefinitionException + * if no {@link ProcessEngine} bean was found. + * @throws NoUniqueBeanDefinitionException + * if more than one {@link ProcessEngine} bean was found. + * @throws BeansException + * if a problem getting the {@link ProcessEngine} bean occurred. + * @throws IllegalStateException + * if the Spring WebApplicationContext was not found, which means that the servlet is used outside a + * Spring web environment. + * @throws IllegalArgumentException + * in case the process instance id referenced by the request parameter + * {@link #getProcessInstanceIdParameterName()} does not exist. + */ + public ExecutionContext getExecutionContext(HttpServletRequest request) { + return getProcessInstance(request).getExecutionContext(); + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractTask.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractTask.java new file mode 100644 index 00000000..446bf690 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/AbstractTask.java @@ -0,0 +1,101 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.process.springweb; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.filter.RequestContextFilter; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.api.idp.process.Task; +import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; + +/** + * Abstract task implementation providing {@link HttpServletRequest} and {@link HttpServletResponse}. + *

+ * Note that this abstract task requires the Spring (web) framework including a {@link RequestContextFilter} to be set + * within {@code web.xml}. + * + *

+ * ...
+ * <filter>
+ *   <filter-name>requestContextFilter</filter-name>
+ *   <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
+ * </filter>
+ * <filter-mapping>
+ *   <filter-name>requestContextFilter</filter-name>
+ *   <url-pattern>/*</url-pattern>
+ * </filter-mapping>
+ * ...
+ * 
+ * + * @author tknall + * @author tlenz + * + */ +public abstract class AbstractTask implements Task { + + /** + * Executes the task providing the underlying {@link ExecutionContext} {@code executionContext} as well as the + * respective {@link HttpServletRequest} and {@link HttpServletResponse}. + * + * @param executionContext + * The execution context (never {@code null}). + * @param request + * The HttpServletRequest (never {@code null}). + * @param response + * The HttpServletResponse (never {@code null}). + * @throws IllegalStateException + * Thrown in case the task is nur being run within the required environment. Refer to javadoc for + * further information. + * @throws Exception + * Thrown in case of error executing the task. + */ + public abstract void execute(ExecutionContext executionContext, HttpServletRequest request, + HttpServletResponse response) throws TaskExecutionException; + + /** + * Executes the task providing the underlying {@link ExecutionContext} {@code executionContext} + * and the {@link IRequest} {@code pendingReq }as well as the + * respective {@link HttpServletRequest} and {@link HttpServletResponse}. + * + * This method sets the pending-request object of the task implementation and starts the + * {@code execute} method of the task + * + * @param pendingReq The pending-request object (never {@code null}). + * @param executionContext The execution context (never {@code null}). + * @param request The HttpServletRequest (never {@code null}). + * @param response The HttpServletResponse (never {@code null}). + * @return The pending-request object, because Process-management works recursive + * + * @throws IllegalStateException + * Thrown in case the task is being run within the required environment. Refer to javadoc for + * further information. + * @throws Exception + * Thrown in case of error executing the task. + */ + protected abstract IRequest internalExecute(IRequest pendingReq, ExecutionContext executionContext, HttpServletRequest request, + HttpServletResponse response) throws TaskExecutionException; + + @Override + public IRequest execute(IRequest pendingReq, ExecutionContext executionContext) throws TaskExecutionException { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes != null && requestAttributes instanceof ServletRequestAttributes) { + HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); + HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse(); + if (request == null || response == null) { + throw new IllegalStateException( + "Spring's RequestContextHolder did not provide HttpServletResponse. Did you forget to set the required org.springframework.web.filter.RequestContextFilter in your web.xml."); + } + return internalExecute(pendingReq, executionContext, request, response); + } else { + throw new IllegalStateException("Task needs to be executed within a Spring web environment."); + } + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/SpringWebExpressionEvaluator.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/SpringWebExpressionEvaluator.java new file mode 100644 index 00000000..229735e3 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/springweb/SpringWebExpressionEvaluator.java @@ -0,0 +1,145 @@ +/******************************************************************************* + *******************************************************************************/ +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 + * + * + * @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.warn("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); + } + +} -- cgit v1.2.3