Skip to content

Commit

Permalink
Support listening to a variable change event with multiple elements i…
Browse files Browse the repository at this point in the history
…n bpmn and cmmn
  • Loading branch information
tijsrademakers committed Feb 9, 2024
1 parent 2d75f7f commit f418d15
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL"
xmlns:flowable="http://flowable.org/cmmn"
targetNamespace="http://flowable.org/cmmn">
<case id="variableListener" name="Variable listener">
<casePlanModel id="casePlanModel">
<planItem id="planItemA" name="A" definitionRef="taskA"/>
<planItem id="variableEventListenerPlanItem" definitionRef="variableEventListener"/>
<planItem id="planItemB" name="B" definitionRef="taskB">
<entryCriterion id="entryTaskB" sentryRef="sentryOnVariableEventListener"/>
</planItem>
<planItem id="variableEventListenerPlanItem2" definitionRef="variableEventListener2"/>
<planItem id="planItemC" name="C" definitionRef="taskC">
<entryCriterion id="entryTaskC" sentryRef="sentryOnVariableEventListener2"/>
</planItem>
<sentry id="sentryOnVariableEventListener">
<planItemOnPart id="sentryOnvariableEvent" sourceRef="variableEventListenerPlanItem">
<standardEvent>occur</standardEvent>
</planItemOnPart>
</sentry>
<sentry id="sentryOnVariableEventListener2">
<planItemOnPart id="sentryOnvariableEvent2" sourceRef="variableEventListenerPlanItem2">
<standardEvent>occur</standardEvent>
</planItemOnPart>
</sentry>
<humanTask id="taskA" name="A"/>
<eventListener id="variableEventListener" flowable:eventType="variable" flowable:variableName="var1" />
<humanTask id="taskB" name="B"/>
<eventListener id="variableEventListener2" flowable:eventType="variable" flowable:variableName="var1" />
<humanTask id="taskC" name="C"/>
</casePlanModel>
</case>
</definitions>
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -22,6 +25,7 @@ public class VariableListenerSessionData {
protected String scopeId;
protected String scopeType;
protected String scopeDefinitionId;
protected List<String> processedElementIds = new ArrayList<>();

public VariableListenerSessionData(String changeType, String scopeId, String scopeType, String scopeDefinitionId) {
this.changeType = changeType;
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples">

<process id="catchVariableListener">

<startEvent id="start" />

<sequenceFlow sourceRef="start" targetRef="parallelFork" />

<parallelGateway id="parallelFork" />

<sequenceFlow sourceRef="parallelFork" targetRef="variableListenerEvent" />

<intermediateCatchEvent id="variableListenerEvent" name="Variable listener">
<extensionElements>
<flowable:variableListenerEventDefinition variableName="var1" />
</extensionElements>
</intermediateCatchEvent>

<userTask id="aftertask" />

<sequenceFlow sourceRef="parallelFork" targetRef="variableListenerEvent2" />

<intermediateCatchEvent id="variableListenerEvent2" name="Variable listener 2">
<extensionElements>
<flowable:variableListenerEventDefinition variableName="var1" />
</extensionElements>
</intermediateCatchEvent>

<userTask id="aftertask2" />

<sequenceFlow sourceRef="variableListenerEvent" targetRef="aftertask" />

<sequenceFlow sourceRef="variableListenerEvent2" targetRef="aftertask2" />

<parallelGateway id="parallelJoin" />

<sequenceFlow sourceRef="aftertask" targetRef="parallelJoin" />

<sequenceFlow sourceRef="aftertask2" targetRef="parallelJoin" />

<endEvent id="end" />

<sequenceFlow sourceRef="parallelJoin" targetRef="end" />

</process>

</definitions>

0 comments on commit f418d15

Please sign in to comment.