Skip to content

Commit

Permalink
fix(engine): prevent NPE when terminating call activity
Browse files Browse the repository at this point in the history
If a parent process has an active call activity element instance there is no guarantee that the called activity is still activate. When the called activity is completed it will try to complete the call activity element. At this point we try to apply the output mappings. If something goes wrong here an incident is created.
When we try to terminate the call activity at this point the called activity is already completed and no instance of it will be available. For this reason we must verify that this instance is not null before we try to terminate it.
  • Loading branch information
remcowesterhoud committed Nov 22, 2022
1 parent 07220cc commit 254a863
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,9 @@ private void terminateElement(
} else if (elementType == BpmnElementType.CALL_ACTIVITY) {
final var calledActivityElementInstance =
elementInstanceState.getInstance(elementInstance.getCalledChildInstanceKey());
terminateElement(calledActivityElementInstance, sideEffects);
if (calledActivityElementInstance != null && calledActivityElementInstance.canTerminate()) {
terminateElement(calledActivityElementInstance, sideEffects);
}
}

stateWriter.appendFollowUpEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.camunda.zeebe.model.bpmn.builder.SubProcessBuilder;
import io.camunda.zeebe.protocol.record.Record;
import io.camunda.zeebe.protocol.record.RecordType;
import io.camunda.zeebe.protocol.record.intent.IncidentIntent;
import io.camunda.zeebe.protocol.record.intent.JobIntent;
import io.camunda.zeebe.protocol.record.intent.ProcessInstanceIntent;
import io.camunda.zeebe.protocol.record.intent.ProcessInstanceModificationIntent;
Expand Down Expand Up @@ -850,6 +851,54 @@ public void shouldActivateParallelGateway() {
.hasSize(4);
}

@Test
public void verifyCallActivityWithIncidentInOutputMappingCanBeTerminated() {
final var child = Bpmn.createExecutableProcess("child").startEvent().endEvent().done();
final var parent =
Bpmn.createExecutableProcess(PROCESS_ID)
.startEvent()
.callActivity("callActivity", c -> c.zeebeProcessId("child"))
.zeebeOutputExpression("x", "y")
.manualTask("task")
.endEvent()
.done();

ENGINE.deployment().withXmlResource(child).withXmlResource(parent).deploy();

final var processInstanceKey = ENGINE.processInstance().ofBpmnProcessId(PROCESS_ID).create();

final var callActivityElement =
RecordingExporter.processInstanceRecords(ProcessInstanceIntent.ELEMENT_ACTIVATED)
.withProcessInstanceKey(processInstanceKey)
.withElementId("callActivity")
.withElementType(BpmnElementType.CALL_ACTIVITY)
.getFirst();

Assertions.assertThat(
RecordingExporter.incidentRecords(IncidentIntent.CREATED)
.withProcessInstanceKey(processInstanceKey)
.getFirst())
.extracting(r -> r.getValue().getElementId())
.isEqualTo("callActivity");

ENGINE
.processInstance()
.withInstanceKey(processInstanceKey)
.modification()
.activateElement("task")
.terminateElement(callActivityElement.getKey())
.modify();

verifyThatRootElementIsActivated(processInstanceKey, "task", BpmnElementType.MANUAL_TASK);
verifyThatProcessInstanceIsCompleted(processInstanceKey);
Assertions.assertThat(
RecordingExporter.processInstanceRecords(ProcessInstanceIntent.ELEMENT_TERMINATED)
.withProcessInstanceKey(processInstanceKey)
.withElementId(callActivityElement.getValue().getElementId())
.exists())
.isTrue();
}

private static void verifyThatRootElementIsActivated(
final long processInstanceKey, final String elementId, final BpmnElementType elementType) {
verifyThatElementIsActivated(processInstanceKey, elementId, elementType, processInstanceKey);
Expand Down

0 comments on commit 254a863

Please sign in to comment.