未验证 提交 82885732 编写于 作者: E Elias Ricken de Medeiros 提交者: GitHub

Fix variable propagation for user tasks (#3788)

In order to keep consistency with service tasks, we also need to update the current execution variables in addition to propagate them to process variables. While evaluating expressions on sequence flows, variables set in the current execution will have higher priority over process variables.

Fixes https://github.com/Activiti/Activiti/issues/3787
上级 47dc61c0
......@@ -52,9 +52,6 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*/
public class UserTaskActivityBehavior extends TaskActivityBehavior {
private static final long serialVersionUID = 1L;
......@@ -255,7 +252,7 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior {
protected Map<String, Object> calculateOutBoundVariables(DelegateExecution execution,
Map<String, Object> taskVariables) {
CommandContext commandContext = Context.getCommandContext();
if(commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()){
if (commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()) {
return taskVariables;
}
return emptyMap();
......@@ -294,6 +291,7 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior {
Map<String, Object> outboundVariables = calculateOutBoundVariables(execution,
taskVariables);
processInstanceEntity.setVariables(outboundVariables);
execution.setVariablesLocal(outboundVariables);
}
}
......
......@@ -162,6 +162,14 @@ public class RuntimeTestConfiguration {
};
}
@Bean(name = "value-processor.process")
public Connector valueProcessorConnector() {
return integrationContext -> {
integrationContext.addOutBoundVariable("providedValue",integrationContext.getInBoundVariable("input"));
return integrationContext;
};
}
@Bean(name = "Tag Image Connector.tagImageActionName")
public Connector tagImageActionName() {
return integrationContext -> {
......
......@@ -15,8 +15,10 @@
*/
package org.activiti.spring.boot.tasks;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.awaitility.Awaitility.await;
import java.util.Date;
import java.util.HashMap;
......@@ -27,6 +29,7 @@ import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.task.model.Task;
import org.activiti.common.util.DateFormatterProvider;
import org.activiti.spring.boot.process.ProcessBaseRuntime;
import org.activiti.spring.boot.security.util.SecurityUtil;
import org.activiti.spring.boot.test.util.ProcessCleanUpUtil;
import org.activiti.spring.boot.test.util.TaskCleanUpUtil;
import org.junit.jupiter.api.AfterEach;
......@@ -49,6 +52,9 @@ public class TaskRuntimeVariableMappingIT {
private static final String TASK_MAP_ALL_PREVALENCE = "taskVariableMappingSendAllPrevalence";
@Autowired
private SecurityUtil securityUtil;
@Autowired
private TaskBaseRuntime taskBaseRuntime;
......@@ -459,4 +465,56 @@ public class TaskRuntimeVariableMappingIT {
assertThat(tasks).hasSize(1);
return tasks.get(0);
}
@Test
public void should_supportVariableMappingAfterLoopingBack() {
//given
final ProcessInstance processInstance = processBaseRuntime.startProcessWithProcessDefinitionKey("Process_N4qkN051N");
List<Task> tasks = taskBaseRuntime.getTasksByProcessInstanceId(processInstance.getId());
assertThat(tasks)
.extracting(Task::getName)
.containsExactly("Enter values");
//when the task completes with a variable value causing a loop back
taskBaseRuntime.completeTask(tasks.get(0), singletonMap("formInput", "provided-it1"));
//then process loops back to the first task
waitForTaskOnProcessInstance(processInstance, "Enter values");
List<VariableInstance> variables = processBaseRuntime.getVariables(processInstance);
assertThat(variables)
.extracting(
VariableInstance::getName,
VariableInstance::getValue
).containsExactly(
tuple("providedValue", "provided-it1")
);
//when the task completes with a variable value not causing a loop back
tasks = taskBaseRuntime.getTasksByProcessInstanceId(
processInstance.getId());
taskBaseRuntime.completeTask(tasks.get(0),
singletonMap("formInput", "go"));
//then the process reaches the next task
waitForTaskOnProcessInstance(processInstance, "Wait");
variables = processBaseRuntime.getVariables(processInstance);
assertThat(variables)
.extracting(
VariableInstance::getName,
VariableInstance::getValue
).containsExactly(
tuple("providedValue", "go")
);
}
private void waitForTaskOnProcessInstance(ProcessInstance processInstance, String taskName) {
await().untilAsserted(() -> {
securityUtil.logInAs("user");
assertThat(taskBaseRuntime.getTasksByProcessInstanceId(processInstance.getId()))
.extracting(Task::getName)
.containsExactly(taskName);
});
}
}
{
"id": "process-fdee92a0-5a2b-48be-8696-245eff49aa07",
"type": "PROCESS",
"name": "variable-mapping-with-loop",
"extensions": {
"Process_N4qkN051N": {
"constants": {},
"mappings": {
"Task_0od1320": {
"outputs": {
"providedValue": {
"type": "variable",
"value": "providedValue"
}
},
"inputs": {
"input": {
"type": "variable",
"value": "providedValue"
}
}
},
"Task_125yjke": {
"outputs": {
"providedValue": {
"type": "variable",
"value": "formInput"
}
}
}
},
"properties": {
"e974d84d-aa76-4acb-903d-97582fe89861": {
"id": "e974d84d-aa76-4acb-903d-97582fe89861",
"name": "providedValue",
"type": "string",
"value": "",
"required": false
}
},
"assignments": {
"Task_125yjke": {
"type": "identity",
"assignment": "assignee",
"id": "Task_125yjke"
}
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:activiti="http://activiti.org/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="model-fdee92a0-5a2b-48be-8696-245eff49aa07" name="variable-mapping-with-loop" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:process id="Process_N4qkN051N" name="variable-mapping-with-loop" isExecutable="true">
<bpmn2:documentation />
<bpmn2:startEvent id="Event_1">
<bpmn2:outgoing>SequenceFlow_1anr2ek</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="SequenceFlow_1anr2ek" sourceRef="Event_1" targetRef="Task_125yjke" />
<bpmn2:userTask id="Task_125yjke" name="Enter values" activiti:formKey="form-29e73efe-5bd6-48c3-8c8d-598cb0ade731" activiti:assignee="user">
<bpmn2:incoming>SequenceFlow_1anr2ek</bpmn2:incoming>
<bpmn2:incoming>SequenceFlow_0g8zsg1</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_0urignn</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="SequenceFlow_0urignn" sourceRef="Task_125yjke" targetRef="Task_0od1320" />
<bpmn2:exclusiveGateway id="ExclusiveGateway_1mah1i8" default="SequenceFlow_0g8zsg1">
<bpmn2:incoming>SequenceFlow_03x97kf</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_1xda66c</bpmn2:outgoing>
<bpmn2:outgoing>SequenceFlow_0g8zsg1</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="SequenceFlow_03x97kf" sourceRef="Task_0od1320" targetRef="ExclusiveGateway_1mah1i8" />
<bpmn2:endEvent id="EndEvent_12fyn3x">
<bpmn2:incoming>SequenceFlow_1b0cog0</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="SequenceFlow_1xda66c" sourceRef="ExclusiveGateway_1mah1i8" targetRef="UserTask_11es6fr">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${(providedValue eq "go")}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="SequenceFlow_0g8zsg1" name="loop back" sourceRef="ExclusiveGateway_1mah1i8" targetRef="Task_125yjke" />
<bpmn2:serviceTask id="Task_0od1320" name="Process values" implementation="value-processor.process">
<bpmn2:incoming>SequenceFlow_0urignn</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_03x97kf</bpmn2:outgoing>
</bpmn2:serviceTask>
<bpmn2:userTask id="UserTask_11es6fr" name="Wait" activiti:assignee="user" activiti:priority="0">
<bpmn2:incoming>SequenceFlow_1xda66c</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_1b0cog0</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="SequenceFlow_1b0cog0" sourceRef="UserTask_11es6fr" targetRef="EndEvent_12fyn3x" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_N4qkN051N">
<bpmndi:BPMNShape id="_BPMNShape_Event_2" bpmnElement="Event_1">
<dc:Bounds x="142" y="212" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1anr2ek_di" bpmnElement="SequenceFlow_1anr2ek">
<di:waypoint x="178" y="230" />
<di:waypoint x="280" y="230" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="UserTask_15oxjtp_di" bpmnElement="Task_125yjke">
<dc:Bounds x="280" y="190" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_0urignn_di" bpmnElement="SequenceFlow_0urignn">
<di:waypoint x="380" y="230" />
<di:waypoint x="440" y="230" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="ExclusiveGateway_1mah1i8_di" bpmnElement="ExclusiveGateway_1mah1i8" isMarkerVisible="true">
<dc:Bounds x="605" y="205" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_03x97kf_di" bpmnElement="SequenceFlow_03x97kf">
<di:waypoint x="540" y="230" />
<di:waypoint x="605" y="230" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_12fyn3x_di" bpmnElement="EndEvent_12fyn3x">
<dc:Bounds x="882" y="212" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1xda66c_di" bpmnElement="SequenceFlow_1xda66c">
<di:waypoint x="655" y="230" />
<di:waypoint x="740" y="230" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_0g8zsg1_di" bpmnElement="SequenceFlow_0g8zsg1">
<di:waypoint x="630" y="205" />
<di:waypoint x="630" y="110" />
<di:waypoint x="330" y="110" />
<di:waypoint x="330" y="190" />
<bpmndi:BPMNLabel>
<dc:Bounds x="459" y="92" width="48" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="ServiceTask_0k1gbu2_di" bpmnElement="Task_0od1320">
<dc:Bounds x="440" y="190" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_11es6fr_di" bpmnElement="UserTask_11es6fr">
<dc:Bounds x="740" y="190" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1b0cog0_di" bpmnElement="SequenceFlow_1b0cog0">
<di:waypoint x="840" y="230" />
<di:waypoint x="882" y="230" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册