/* * Copyright (c) NASK, NCSC * * This file is part of HoneySpider Network 2.1. * * This is a free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package pl.nask.hsn2.activiti.behavior; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.activiti.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; import org.activiti.engine.impl.pvm.PvmProcessDefinition; import org.activiti.engine.impl.pvm.PvmProcessInstance; import org.activiti.engine.impl.pvm.delegate.ActivityExecution; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.nask.hsn2.bus.connector.process.ProcessConnectorException; import pl.nask.hsn2.bus.operations.TaskErrorReasonType; import pl.nask.hsn2.expressions.EvaluationException; import pl.nask.hsn2.expressions.ExpressionResolver; import pl.nask.hsn2.framework.suppressor.JobSuppressorHelper; import pl.nask.hsn2.framework.workflow.engine.ProcessDefinitionRegistry; import pl.nask.hsn2.framework.workflow.hwl.Output; import pl.nask.hsn2.framework.workflow.hwl.ServiceParam; import pl.nask.hsn2.framework.workflow.job.TasksStatistics; import pl.nask.hsn2.suppressor.JobSuppressorUtils; import pl.nask.hsn2.workflow.engine.ExecutionWrapper; import pl.nask.hsn2.workflow.engine.SubprocessParameters; public final class ServiceBehavior extends AbstractBpmnActivityBehavior implements HSNBehavior { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBehavior.class); private final String serviceName; private final String serviceLabel; private final Properties serviceParameters; private final List<Output> outputs; private final ExpressionResolver expressionResolver; private ProcessDefinitionRegistry<PvmProcessDefinition> definitionRegistry; private List<TaskErrorReasonType> errorsIgnored; public ServiceBehavior(String serviceName, String serviceLabel, Properties parameters, List<Output> outputs, ExpressionResolver resolver, ProcessDefinitionRegistry<PvmProcessDefinition> definitionRegistry,List<TaskErrorReasonType> ignoreErrors) { this.serviceName = serviceName; this.serviceLabel = serviceLabel; this.serviceParameters = parameters; this.outputs = outputs; this.expressionResolver = resolver; this.definitionRegistry = definitionRegistry; if(ignoreErrors !=null) { this.errorsIgnored = ignoreErrors; } else { this.errorsIgnored = new ArrayList<TaskErrorReasonType>(); } } @Override public void execute(ActivityExecution execution) { LOGGER.debug("Executing activity {}", execution.getActivity().getId()); ExecutionWrapper wrapper = new ExecutionWrapper(execution); Long jobId = wrapper.getJobId(); if (jobId == null) { throw new IllegalStateException("No job_id found for the execution: " + execution); } int taskId = wrapper.setNewTaskId(); Properties params = ServiceParam.merge(serviceParameters, wrapper.getUserConfig().get(serviceLabel), true); SubprocessParameters processParams = wrapper.getSubprocessParameters(); long objectDataId = (processParams != null) ? processParams.getObjectDataId() : 0L; TasksStatistics stats = wrapper.getJobStats(); JobSuppressorHelper jobSuppressorHelper = wrapper.getProcessContext().getJobSuppressorHelper(); if (jobSuppressorHelper == null) { // Suppressor is disabled. try { JobSuppressorUtils.sendTaskRequest(jobId, stats, serviceName, serviceLabel, taskId, objectDataId, params); } catch (ProcessConnectorException e) { LOGGER.error(e.getMessage(), e); throw new FatalTaskErrorException("Non-ignored task error: ", e); } } else { // Suppressor is enabled. wrapper.getProcessContext().getJobSuppressorHelper() .addTaskRequest(serviceName, serviceLabel, taskId, objectDataId, params, stats); } } @Override public void signal(ActivityExecution execution, String signalName, Object signalData) throws EvaluationException { // TODO: inactive executions should not process the signal. the signal should be passed to the subprocess instead. // TODO: all signals should be passed to subexecutions? ExecutionWrapper wrapper = new ExecutionWrapper(execution); JobSuppressorHelper jobSuppressorHelper = wrapper.getProcessContext().getJobSuppressorHelper(); LOGGER.debug("activity: {}", execution.getActivity().getId()); LOGGER.debug("got signal: {} with data: {}", signalName, signalData); if (!execution.isActive()) { LOGGER.debug("Execution not active, ignoring the signal"); } if ("completeTask".equalsIgnoreCase(signalName)) { completeTask(execution, signalData); if (jobSuppressorHelper != null) { // Suppressor is enabled. jobSuppressorHelper.signalTaskCompletion(wrapper.getJobId(), wrapper.getTaskId(), wrapper.getJobStats()); } } else if ("subprocess".equalsIgnoreCase(signalName)) { runSubprocesses(execution, signalData); } else if ("taskFailed".equalsIgnoreCase(signalName)) { handleTaskFailed(execution,(Object [])signalData); if (jobSuppressorHelper != null) { // Suppressor is enabled. jobSuppressorHelper.signalTaskCompletion(wrapper.getJobId(), wrapper.getTaskId(), wrapper.getJobStats()); } } else { LOGGER.debug("Unknown signal name, ignore: {}",signalName); } } private void handleTaskFailed(ActivityExecution execution, Object[] signalData) { int reqId = (Integer) signalData[0]; TaskErrorReasonType errorType = (TaskErrorReasonType) signalData[1]; String errorDescription = (String)signalData[2]; if(!errorsIgnored.contains(errorType)) { throw new FatalTaskErrorException("Non-ignored task error:"+errorType.name()); } else { LOGGER.info("Ignoring TaskError:[reqId={},descr={}]",reqId,errorDescription); ExecutionWrapper wrapper = new ExecutionWrapper(execution); TasksStatistics stats = wrapper.getJobStats(); if (stats != null) { stats.taskCompleted(serviceName); } leave(execution); } } private void runSubprocesses(ActivityExecution execution, Object signalData) throws EvaluationException { LOGGER.debug("run subprocesses"); if (outputs == null || outputs.isEmpty()) return; for (Output output: outputs) { if (isOutputExpressionTrue(execution, signalData, output)) runSubprocess(execution, signalData, output); } } private boolean isOutputExpressionTrue(ActivityExecution execution, Object signalData, Output output) throws EvaluationException { String expression = output.getExpression(); if (expression == null) return true; ExecutionWrapper wrapper = new ExecutionWrapper(execution); try { return expressionResolver.evaluateBoolean(wrapper.getJobId(), ((SubprocessParameters) signalData).getObjectDataId(), expression); } catch (Exception e) { LOGGER.warn("Error evaluating output condition for {}, returning false", output); LOGGER.warn("Error evaluating output condition", e); return false; } } private void runSubprocess(ActivityExecution execution, Object signalData, Output output) { LOGGER.debug("run subprocesses"); PvmProcessDefinition definition = definitionRegistry.getDefinition(output.getProcessName()); PvmProcessInstance subprocess = definition.createProcessInstance(); // set parent to the current execution/process ExecutionWrapper subProcessWrapper = new ExecutionWrapper(subprocess); subProcessWrapper.initProcessStateFrom(execution, (SubprocessParameters) signalData); subprocess.start(); } private void completeTask(ActivityExecution execution, Object signalData) { LOGGER.debug("TaskCompleted"); ExecutionWrapper wrapper = new ExecutionWrapper(execution); TasksStatistics stats = wrapper.getJobStats(); if (stats != null) { stats.taskCompleted(serviceName); } leave(execution); } @Override public String toString() { return "ServiceBehavior (" + serviceName + ")"; } @Override public String getStepName() { return "service(" + serviceName + ")"; } }