/* * Copyright 2015 herd contributors * * 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 org.finra.herd.service.activiti; import org.activiti.engine.RuntimeService; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.runtime.Execution; import org.activiti.engine.runtime.ExecutionQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * A helper for activiti's {@link RuntimeService}. * <p/> * This class is distinguised from {@link ActivitiHelper} in that it has direct access to a RuntimeService. This class was separated from ActivitiHelper * implementation due to a circular dependency introduced if RuntimeService is autowired into ActivitiHelper. The circular dependency is as thus: * <p/> * RuntimeService -> ProcessEngine -> ProcessEngineConfiguration -> HerdCommandInvoker -> ActivitiHelper -> RuntimeService * <p/> * So instead, this class breaks the circularity: * <p/> * RuntimeService -> ProcessEngine -> ProcessEngineConfiguration -> HerdCommandInvoker -> ActivitiHelper ActivitiRuntimeHelper -> RuntimeService * <p/> * Due to historical reasons, some methods that were previously in ActivitiHelper has been moved into this class. */ @Component public class ActivitiRuntimeHelper { private static final Logger LOGGER = LoggerFactory.getLogger(ActivitiRuntimeHelper.class); public static final String VARIABLE_STATUS = "taskStatus"; public static final String VARIABLE_ERROR_MESSAGE = "taskErrorMessage"; public static final String TASK_STATUS_SUCCESS = "SUCCESS"; public static final String TASK_STATUS_ERROR = "ERROR"; public static final String TASK_VARIABLE_MARKER = "_"; @Autowired private RuntimeService runtimeService; /** * Signals a task by its process instance ID and the ID of the task to signal. * * @param processInstanceId The process instance ID of the waiting task. * @param signalTaskId The ID of the task to signal. */ public void signal(String processInstanceId, String signalTaskId) { ExecutionQuery signalTaskQuery = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).activityId(signalTaskId); Execution execution = signalTaskQuery.singleResult(); runtimeService.signal(execution.getId()); } /** * Sets the workflow variable whose name references the task which produced the variable. This method will automatically generate and concatenate a name to * the variable name which can help reference the task which produces the variable. * * @param executionId The execution ID * @param activitiId The task's ID * @param variableName Name of the variable without the task reference * @param variableValue The value of the variable. */ public void setTaskWorkflowVariable(String executionId, String activitiId, String variableName, Object variableValue) { // Get the variables associated with this execution. This is a workaround to what appears to be an Activiti bug in version 5.19.0.1 (which used // to work in 5.16.3). In the newer version, setting a variable like we are doing below doesn't actually set the variable properly in // VariableScopeImpl.createVariableInstance because an internal variableInstances hasn't yet been initialized. By calling the getVariables // method, that forces that map to get initialized and the subsequent setVariable method below will work. runtimeService.getVariables(executionId); runtimeService.setVariable(executionId, buildTaskWorkflowVariableName(activitiId, variableName), variableValue); } /** * Sets a workflow variable name based on the template. * * @param execution the workflow execution * @param variableName the variable name * @param variableValue the variable value */ public void setTaskWorkflowVariable(DelegateExecution execution, String variableName, Object variableValue) { setTaskWorkflowVariable(execution.getId(), execution.getCurrentActivityId(), variableName, variableValue); } /** * Sets the task success status in a workflow variable. * * @param execution the workflow execution. */ public void setTaskSuccessInWorkflow(DelegateExecution execution) { setTaskWorkflowVariable(execution, VARIABLE_STATUS, TASK_STATUS_SUCCESS); } /** * Sets the task error status and message in workflow variables. * * @param execution the workflow execution. * @param errorMessage the error message. * @param exception an optional exception that caused the error. */ public void setTaskErrorInWorkflow(DelegateExecution execution, String errorMessage, Exception exception) { setTaskWorkflowVariable(execution, VARIABLE_STATUS, TASK_STATUS_ERROR); setTaskWorkflowVariable(execution, VARIABLE_ERROR_MESSAGE, errorMessage); if (exception != null) { LOGGER.warn("Workflow encountered an error. Logging stack trace in case it is needed to debug an issue. " + "activitiCurrentActivityId=\"{}\" activitiExecutionId=\"{}\"", execution.getCurrentActivityId(), execution.getId(), exception); // TODO: Remove this logging statement above and set a workflow variable with the stack trace instead. // setTaskWorkflowVariable(execution, "taskErrorStackTrace", ExceptionUtils.getStackTrace(exception)); } } /** * Builds a workflow variable name based on the template. * * @param activitiId activitiId * @param variableName the variable name * * @return the workflow variable name */ public String buildTaskWorkflowVariableName(String activitiId, String variableName) { return activitiId + TASK_VARIABLE_MARKER + variableName; } }