diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateVariableEventListenersOperation.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateVariableEventListenersOperation.java
index c5ef2fec146..737b716b092 100644
--- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateVariableEventListenersOperation.java
+++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/EvaluateVariableEventListenersOperation.java
@@ -93,10 +93,12 @@ public void run() {
if (changeTypeValue.equals(variableListenerData.getChangeType()) || VariableListenerEventDefinition.CHANGE_TYPE_ALL.equals(changeTypeValue) ||
(VariableListenerEventDefinition.CHANGE_TYPE_UPDATE_CREATE.equals(changeTypeValue) &&
(VariableListenerEventDefinition.CHANGE_TYPE_CREATE.equals(variableListenerData.getChangeType()) || VariableListenerEventDefinition.CHANGE_TYPE_UPDATE.equals(variableListenerData.getChangeType())))) {
-
- itVariableListener.remove();
- CommandContextUtil.getAgenda().planTriggerPlanItemInstanceOperation(planItemInstance);
- triggeredPlanItemInstance = true;
+
+ if (!variableListenerData.containsProcessedElementId(planItemInstance.getPlanItemDefinitionId())) {
+ CommandContextUtil.getAgenda().planTriggerPlanItemInstanceOperation(planItemInstance);
+ triggeredPlanItemInstance = true;
+ variableListenerData.addProcessedElementId(planItemInstance.getPlanItemDefinitionId());
+ }
}
}
}
diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.java
index 1d08e3b7f26..ce4761f5605 100644
--- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.java
+++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.java
@@ -196,6 +196,74 @@ public void testTriggerVariableEventListenerInStageOnlyCreate() {
assertCaseInstanceEnded(caseInstance);
}
+
+ @Test
+ @CmmnDeployment
+ public void testTriggerMultipleVariableEventListeners() {
+ CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("variableListener").start();
+
+ // 5 plan items reachable
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().count()).isEqualTo(5);
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().list())
+ .extracting(PlanItemInstance::getPlanItemDefinitionType, PlanItemInstance::getPlanItemDefinitionId, PlanItemInstance::getState)
+ .containsExactlyInAnyOrder(
+ tuple(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER, "variableEventListener", PlanItemInstanceState.AVAILABLE),
+ tuple(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER, "variableEventListener2", PlanItemInstanceState.AVAILABLE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskA", PlanItemInstanceState.ACTIVE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskB", PlanItemInstanceState.AVAILABLE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskC", PlanItemInstanceState.AVAILABLE)
+ );
+
+ if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) {
+ assertThat(cmmnHistoryService.createHistoricPlanItemInstanceQuery().list())
+ .extracting(HistoricPlanItemInstance::getPlanItemDefinitionType, HistoricPlanItemInstance::getPlanItemDefinitionId, HistoricPlanItemInstance::getState)
+ .containsExactlyInAnyOrder(
+ tuple(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER, "variableEventListener", PlanItemInstanceState.AVAILABLE),
+ tuple(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER, "variableEventListener2", PlanItemInstanceState.AVAILABLE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskA", PlanItemInstanceState.ACTIVE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskB", PlanItemInstanceState.AVAILABLE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskC", PlanItemInstanceState.AVAILABLE)
+ );
+ }
+
+ // create different variable
+ cmmnRuntimeService.setVariable(caseInstance.getId(), "var2", "test");
+
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionType(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER).count()).isEqualTo(2);
+
+ // create var1 variable to trigger variable event listener
+ cmmnRuntimeService.setVariable(caseInstance.getId(), "var1", "test");
+
+ // variable event listener should be completed
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionType(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER).count()).isZero();
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().list())
+ .extracting(PlanItemInstance::getPlanItemDefinitionType, PlanItemInstance::getPlanItemDefinitionId, PlanItemInstance::getState)
+ .containsExactlyInAnyOrder(
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskA", PlanItemInstanceState.ACTIVE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskB", PlanItemInstanceState.ACTIVE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskC", PlanItemInstanceState.ACTIVE)
+ );
+
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionId("taskB").planItemInstanceStateActive().count()).isEqualTo(1);
+ assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionId("taskC").planItemInstanceStateActive().count()).isEqualTo(1);
+
+ if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) {
+ assertThat(cmmnHistoryService.createHistoricPlanItemInstanceQuery().list())
+ .extracting(HistoricPlanItemInstance::getPlanItemDefinitionType, HistoricPlanItemInstance::getPlanItemDefinitionId, HistoricPlanItemInstance::getState)
+ .containsExactlyInAnyOrder(
+ tuple(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER, "variableEventListener", PlanItemInstanceState.COMPLETED),
+ tuple(PlanItemDefinitionType.VARIABLE_EVENT_LISTENER, "variableEventListener2", PlanItemInstanceState.COMPLETED),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskA", PlanItemInstanceState.ACTIVE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskB", PlanItemInstanceState.ACTIVE),
+ tuple(PlanItemDefinitionType.HUMAN_TASK, "taskC", PlanItemInstanceState.ACTIVE)
+ );
+ }
+
+
+ assertCaseInstanceNotEnded(caseInstance);
+ cmmnTaskService.createTaskQuery().list().forEach(t -> cmmnTaskService.complete(t.getId()));
+ assertCaseInstanceEnded(caseInstance);
+ }
@Test
@CmmnDeployment
diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.testTriggerMultipleVariableEventListeners.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.testTriggerMultipleVariableEventListeners.cmmn
new file mode 100644
index 00000000000..8b1390ca4cc
--- /dev/null
+++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/eventlistener/VariableEventListenerTest.testTriggerMultipleVariableEventListeners.cmmn
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ occur
+
+
+
+
+ occur
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/variablelistener/VariableListenerSessionData.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/variablelistener/VariableListenerSessionData.java
index d21d3d5e916..99049a3af51 100644
--- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/variablelistener/VariableListenerSessionData.java
+++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/variablelistener/VariableListenerSessionData.java
@@ -12,6 +12,9 @@
*/
package org.flowable.common.engine.impl.variablelistener;
+import java.util.ArrayList;
+import java.util.List;
+
public class VariableListenerSessionData {
public static final String VARIABLE_CREATE = "create";
@@ -22,6 +25,7 @@ public class VariableListenerSessionData {
protected String scopeId;
protected String scopeType;
protected String scopeDefinitionId;
+ protected List processedElementIds = new ArrayList<>();
public VariableListenerSessionData(String changeType, String scopeId, String scopeType, String scopeDefinitionId) {
this.changeType = changeType;
@@ -57,5 +61,11 @@ public String getScopeDefinitionId() {
}
public void setScopeDefinitionId(String scopeDefinitionId) {
this.scopeDefinitionId = scopeDefinitionId;
- }
+ }
+ public boolean containsProcessedElementId(String elementId) {
+ return this.processedElementIds.contains(elementId);
+ }
+ public void addProcessedElementId(String elementId) {
+ this.processedElementIds.add(elementId);
+ }
}
diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/agenda/EvaluateVariableListenerEventDefinitionsOperation.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/agenda/EvaluateVariableListenerEventDefinitionsOperation.java
index 8691987d497..ac6e2bce53a 100644
--- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/agenda/EvaluateVariableListenerEventDefinitionsOperation.java
+++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/agenda/EvaluateVariableListenerEventDefinitionsOperation.java
@@ -106,8 +106,10 @@ public void run() {
VariableListenerEventDefinition.CHANGE_TYPE_ALL.equals(changeTypeValue) || (VariableListenerEventDefinition.CHANGE_TYPE_UPDATE_CREATE.equals(changeTypeValue) &&
(VariableListenerEventDefinition.CHANGE_TYPE_CREATE.equals(variableListenerData.getChangeType()) || VariableListenerEventDefinition.CHANGE_TYPE_UPDATE.equals(variableListenerData.getChangeType())))) {
- itVariableListener.remove();
- CommandContextUtil.getAgenda().planTriggerExecutionOperation(execution);
+ if (!variableListenerData.containsProcessedElementId(execution.getActivityId())) {
+ CommandContextUtil.getAgenda().planTriggerExecutionOperation(execution);
+ variableListenerData.addProcessedElementId(execution.getActivityId());
+ }
}
}
}
diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.java
index 6291fc4fb72..15661b7efc0 100644
--- a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.java
+++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.java
@@ -88,6 +88,28 @@ public void catchVariableListenerUpdate() {
assertThat(runtimeService.createExecutionQuery().processInstanceId(processInstance.getId()).count()).isEqualTo(0);
}
+ @Test
+ @Deployment
+ public void multipleCatchVariableListeners() {
+ ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("catchVariableListener");
+
+ assertThat(runtimeService.createEventSubscriptionQuery().processInstanceId(processInstance.getId()).list()).hasSize(2);
+
+ assertThat(runtimeService.createExecutionQuery().processInstanceId(processInstance.getId()).count()).isEqualTo(3);
+
+ runtimeService.setVariable(processInstance.getId(), "var2", "test");
+
+ assertThat(runtimeService.createEventSubscriptionQuery().processInstanceId(processInstance.getId()).list()).hasSize(2);
+
+ runtimeService.setVariable(processInstance.getId(), "var1", "test");
+
+ assertThat(runtimeService.createEventSubscriptionQuery().processInstanceId(processInstance.getId()).list()).hasSize(0);
+
+ assertThat(taskService.createTaskQuery().processInstanceId(processInstance.getId()).count()).isEqualTo(2);
+ assertThat(taskService.createTaskQuery().taskDefinitionKey("aftertask").processInstanceId(processInstance.getId()).count()).isEqualTo(1);
+ assertThat(taskService.createTaskQuery().taskDefinitionKey("aftertask2").processInstanceId(processInstance.getId()).count()).isEqualTo(1);
+ }
+
@Test
@Deployment
public void boundaryVariableListener() {
diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.multipleCatchVariableListeners.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.multipleCatchVariableListeners.bpmn20.xml
new file mode 100644
index 00000000000..f3595d15fc9
--- /dev/null
+++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/event/variable/VariableListenerEventTest.multipleCatchVariableListeners.bpmn20.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+