This is an automated email from the ASF dual-hosted git repository.
pefernan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie.git
The following commit(s) were added to refs/heads/main by this push:
new 468ac2d975a [incubator-kie#7206] Remove fallback to sequential mode in
ForEachNodeInstance with async nodes (#7208)
468ac2d975a is described below
commit 468ac2d975aec16538fba1ac878b31422b82e25c
Author: Martin Weiler <[email protected]>
AuthorDate: Fri Oct 30 05:40:19 2026 -0600
[incubator-kie#7206] Remove fallback to sequential mode in
ForEachNodeInstance with async nodes (#7208)
* [kie-issues#7206] Remove fallback to sequential mode in
ForEachNodeInstance with async nodes
* Update the javadoc of the new test class
* Review feedback: Add process diagram information and fix imports
---
.../instance/node/ForEachNodeInstance.java | 23 ++-
.../node/LambdaSubProcessNodeInstance.java | 15 --
.../org/jbpm/bpmn2/foreach/MiChildProcess.bpmn2 | 166 +++++++++++++++++++++
.../org/jbpm/bpmn2/foreach/MiParentProcess.bpmn2 | 90 +++++++++++
.../bpmn2/ForEachParallelSubProcessAsyncTest.java | 87 +++++++++++
5 files changed, 353 insertions(+), 28 deletions(-)
diff --git
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/ForEachNodeInstance.java
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/ForEachNodeInstance.java
index 4749aee86ed..0e30fb09e0e 100755
---
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/ForEachNodeInstance.java
+++
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/ForEachNodeInstance.java
@@ -40,7 +40,6 @@ import org.jbpm.process.instance.impl.ReturnValueEvaluator;
import org.jbpm.ruleflow.core.Metadata;
import org.jbpm.util.ContextFactory;
import org.jbpm.workflow.core.Node;
-import org.jbpm.workflow.core.node.AsyncEventNodeInstance;
import org.jbpm.workflow.core.node.ForEachNode;
import org.jbpm.workflow.core.node.ForEachNode.ForEachJoinNode;
import org.jbpm.workflow.core.node.ForEachNode.ForEachSplitNode;
@@ -68,7 +67,6 @@ public class ForEachNodeInstance extends
CompositeContextNodeInstance {
private int totalInstances;
private int executedInstances;
- boolean hasAsyncInstances;
public ForEachNode getForEachNode() {
return (ForEachNode) getNode();
@@ -90,12 +88,19 @@ public class ForEachNodeInstance extends
CompositeContextNodeInstance {
this.totalInstances = totalInstances;
}
+ /**
+ * Retained for serialisation compatibility only — the async-fallback
logic has been removed.
+ * The field is still present in the protobuf schema (field 5) to avoid
wire-format breakage.
+ */
+ @Deprecated
public boolean getHasAsyncInstances() {
- return hasAsyncInstances;
+ return false;
}
+ /** @see #getHasAsyncInstances() */
+ @Deprecated
public void setHasAsyncInstances(boolean hasAsyncInstances) {
- this.hasAsyncInstances = hasAsyncInstances;
+ // no-op: field is kept in the wire format but is no longer used at
runtime
}
@Override
@@ -138,7 +143,7 @@ public class ForEachNodeInstance extends
CompositeContextNodeInstance {
}
private boolean isSequential() {
- return getForEachNode().isSequential() || hasAsyncInstances;
+ return getForEachNode().isSequential();
}
public class ForEachSplitNodeInstance extends NodeInstanceImpl implements
ContextableInstance {
@@ -172,8 +177,6 @@ public class ForEachNodeInstance extends
CompositeContextNodeInstance {
logger.debug("Triggering [{}] in multi-instance loop.",
nodeInstance.getNodeId());
nodeInstance.trigger(this,
getForEachSplitNode().getTo().getToType());
- //this is required because Parallel instances execution
does not work with async, so it fallbacks to sequential
- hasAsyncInstances = checkAsyncInstance(nodeInstance);
if (isSequential()) {
// for sequential mode trigger only first item from
the list
break;
@@ -225,12 +228,6 @@ public class ForEachNodeInstance extends
CompositeContextNodeInstance {
}
}
- private boolean checkAsyncInstance(NodeInstance nodeInstance) {
- return ((CompositeContextNodeInstance)
nodeInstance).getNodeInstances().stream()
- .anyMatch(i -> i instanceof AsyncEventNodeInstance
- || (i instanceof LambdaSubProcessNodeInstance &&
((LambdaSubProcessNodeInstance) i).isAsyncWaitingNodeInstance()));
- }
-
public class ForEachJoinNodeInstance extends NodeInstanceImpl implements
ContextableInstance {
private static final long serialVersionUID = 510l;
diff --git
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/LambdaSubProcessNodeInstance.java
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/LambdaSubProcessNodeInstance.java
index 7220d0c72c6..c7578ca5f7e 100755
---
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/LambdaSubProcessNodeInstance.java
+++
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/LambdaSubProcessNodeInstance.java
@@ -37,7 +37,6 @@ import
org.jbpm.process.instance.context.exception.ExceptionScopeInstance;
import org.jbpm.process.instance.impl.ContextInstanceFactory;
import org.jbpm.process.instance.impl.ContextInstanceFactoryRegistry;
import org.jbpm.process.instance.impl.ProcessInstanceImpl;
-import org.jbpm.ruleflow.core.Metadata;
import org.jbpm.workflow.core.Node;
import org.jbpm.workflow.core.impl.NodeIoHelper;
import org.jbpm.workflow.core.node.SubProcessNode;
@@ -65,7 +64,6 @@ public class LambdaSubProcessNodeInstance extends
StateBasedNodeInstance impleme
private Map<String, List<ContextInstance>> subContextInstances = new
HashMap<>();
private String processInstanceId;
- private boolean asyncWaitingNodeInstance;
protected SubProcessNode getSubProcessNode() {
return (SubProcessNode) getNode();
@@ -117,7 +115,6 @@ public class LambdaSubProcessNodeInstance extends
StateBasedNodeInstance impleme
subProcessInstance.start();
this.processInstanceId = subProcessInstance.id();
- this.asyncWaitingNodeInstance = hasAsyncNodeInstance(pi);
if (!getSubProcessNode().isWaitForCompletion()) {
triggerCompleted();
@@ -133,14 +130,6 @@ public class LambdaSubProcessNodeInstance extends
StateBasedNodeInstance impleme
}
}
- private boolean
hasAsyncNodeInstance(org.kie.api.runtime.process.ProcessInstance pi) {
- return Optional.ofNullable(pi)
- .map(ProcessInstanceImpl.class::cast)
- .map(instance ->
instance.getMetaData().get(Metadata.ASYNC_WAITING))
- .map(Boolean.class::cast)
- .orElse(false);
- }
-
@Override
public void cancel(CancelType cancelType) {
super.cancel(cancelType);
@@ -163,10 +152,6 @@ public class LambdaSubProcessNodeInstance extends
StateBasedNodeInstance impleme
return processInstanceId;
}
- public boolean isAsyncWaitingNodeInstance() {
- return asyncWaitingNodeInstance;
- }
-
public void internalSetProcessInstanceId(String processInstanceId) {
this.processInstanceId = processInstanceId;
}
diff --git
a/kogito-jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/foreach/MiChildProcess.bpmn2
b/kogito-jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/foreach/MiChildProcess.bpmn2
new file mode 100644
index 00000000000..809076b1803
--- /dev/null
+++
b/kogito-jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/foreach/MiChildProcess.bpmn2
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+
+<!--
+ Child subprocess for the parallel MI ForEach test.
+
+ The process layout exercises the fixed regression:
+ - One customAsync script task (sets ASYNC_WAITING on the process metadata)
+ - Two additional user tasks (wait states) in parallel branches
+
+ The presence of the user tasks means the subprocess is NOT imminently
completing
+ via the async job. Before the fix, hasAsyncNodeInstance() returned true
solely
+ because ASYNC_WAITING was set, forcing the parent ForEach into sequential
mode.
+ After the fix it correctly returns false when wait-state nodes are also
active.
+-->
+
+<definitions id="MiChildProcessDef"
+ targetNamespace="http://www.example.org/MinimalExample"
+ typeLanguage="http://www.java.com/javaTypes"
+ expressionLanguage="http://www.mvel.org/2.0"
+ xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL
BPMN20.xsd"
+ 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"
+ xmlns:tns="http://www.jboss.org/drools">
+
+ <itemDefinition id="_itemItem" structureRef="Integer"/>
+
+ <process processType="Public" isExecutable="true" id="MiChildProcess"
+ name="MI Child Process" tns:packageName="org.jbpm.bpmn2.foreach">
+
+ <property id="item" itemSubjectRef="_itemItem"/>
+
+ <startEvent id="_start" name="Start"/>
+
+ <!-- Parallel split: async branch + two user-task wait-state branches -->
+ <parallelGateway id="_split" gatewayDirection="Diverging"/>
+
+ <!-- Branch 1: customAsync script task → user task wait state -->
+ <scriptTask id="_async" name="Async Script Task"
scriptFormat="http://www.java.com/java">
+ <extensionElements>
+ <tns:metaData name="customAsync">
+ <tns:metaValue><![CDATA[true]]></tns:metaValue>
+ </tns:metaData>
+ </extensionElements>
+ <script>// async continuation</script>
+ </scriptTask>
+ <userTask id="_waitC" name="Wait Task C">
+ <ioSpecification>
+ <dataInput id="_waitC_TaskName" name="TaskName"/>
+ <inputSet><dataInputRefs>_waitC_TaskName</dataInputRefs></inputSet>
+ </ioSpecification>
+ <dataInputAssociation>
+ <targetRef>_waitC_TaskName</targetRef>
+ <assignment>
+ <from xsi:type="tFormalExpression"><![CDATA[WaitTaskC]]></from>
+ <to xsi:type="tFormalExpression"><![CDATA[_waitC_TaskName]]></to>
+ </assignment>
+ </dataInputAssociation>
+ </userTask>
+
+ <!-- Branch 2: user task wait state -->
+ <userTask id="_waitA" name="Wait Task A">
+ <ioSpecification>
+ <dataInput id="_waitA_TaskName" name="TaskName"/>
+ <inputSet><dataInputRefs>_waitA_TaskName</dataInputRefs></inputSet>
+ </ioSpecification>
+ <dataInputAssociation>
+ <targetRef>_waitA_TaskName</targetRef>
+ <assignment>
+ <from xsi:type="tFormalExpression"><![CDATA[WaitTaskA]]></from>
+ <to xsi:type="tFormalExpression"><![CDATA[_waitA_TaskName]]></to>
+ </assignment>
+ </dataInputAssociation>
+ </userTask>
+
+ <!-- Parallel join -->
+ <parallelGateway id="_join" gatewayDirection="Converging"/>
+
+ <endEvent id="_end" name="End">
+ <terminateEventDefinition/>
+ </endEvent>
+
+ <sequenceFlow id="_start-_split" sourceRef="_start" targetRef="_split"/>
+ <sequenceFlow id="_split-_async" sourceRef="_split" targetRef="_async"/>
+ <sequenceFlow id="_split-_waitA" sourceRef="_split" targetRef="_waitA"/>
+ <sequenceFlow id="_async-_waitC" sourceRef="_async" targetRef="_waitC"/>
+ <sequenceFlow id="_waitC-_join" sourceRef="_waitC" targetRef="_join"/>
+ <sequenceFlow id="_waitA-_join" sourceRef="_waitA" targetRef="_join"/>
+ <sequenceFlow id="_join-_end" sourceRef="_join" targetRef="_end"/>
+ </process>
+
+ <bpmndi:BPMNDiagram>
+ <bpmndi:BPMNPlane bpmnElement="MiChildProcess">
+ <bpmndi:BPMNShape bpmnElement="_start">
+ <dc:Bounds x="16" y="56" width="48" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_split">
+ <dc:Bounds x="96" y="56" width="48" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_async">
+ <dc:Bounds x="176" y="16" width="120" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_waitC">
+ <dc:Bounds x="328" y="16" width="100" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_waitA">
+ <dc:Bounds x="252" y="96" width="100" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_join">
+ <dc:Bounds x="460" y="56" width="48" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_end">
+ <dc:Bounds x="540" y="56" width="48" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNEdge bpmnElement="_start-_split">
+ <di:waypoint x="40" y="80"/>
+ <di:waypoint x="120" y="80"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_split-_async">
+ <di:waypoint x="120" y="80"/>
+ <di:waypoint x="236" y="40"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_split-_waitA">
+ <di:waypoint x="120" y="80"/>
+ <di:waypoint x="302" y="120"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_async-_waitC">
+ <di:waypoint x="236" y="40"/>
+ <di:waypoint x="378" y="40"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_waitC-_join">
+ <di:waypoint x="378" y="40"/>
+ <di:waypoint x="484" y="80"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_waitA-_join">
+ <di:waypoint x="302" y="120"/>
+ <di:waypoint x="484" y="80"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_join-_end">
+ <di:waypoint x="484" y="80"/>
+ <di:waypoint x="564" y="80"/>
+ </bpmndi:BPMNEdge>
+ </bpmndi:BPMNPlane>
+ </bpmndi:BPMNDiagram>
+
+</definitions>
diff --git
a/kogito-jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/foreach/MiParentProcess.bpmn2
b/kogito-jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/foreach/MiParentProcess.bpmn2
new file mode 100644
index 00000000000..ffdd8c7cfae
--- /dev/null
+++
b/kogito-jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/foreach/MiParentProcess.bpmn2
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+
+<definitions id="MiParentProcessDef"
+ targetNamespace="http://www.example.org/MinimalExample"
+ typeLanguage="http://www.java.com/javaTypes"
+ expressionLanguage="http://www.mvel.org/2.0"
+ xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL
BPMN20.xsd"
+ 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"
+ xmlns:tns="http://www.jboss.org/drools">
+
+ <itemDefinition id="_itemListItem" structureRef="java.util.List"/>
+
+ <process processType="Public" isExecutable="true" id="MiParentProcess"
+ name="MI Parent Process" tns:packageName="org.jbpm.bpmn2.foreach">
+
+ <property id="items" itemSubjectRef="_itemListItem"/>
+
+ <startEvent id="_start" name="Start"/>
+ <callActivity id="_mi" name="Parallel MI SubProcess"
calledElement="MiChildProcess">
+ <ioSpecification>
+ <dataInput id="_mi_inColl" name="IN_COLLECTION"/>
+ <dataInput id="_mi_item" name="item"/>
+ <inputSet>
+ <dataInputRefs>_mi_inColl</dataInputRefs>
+ <dataInputRefs>_mi_item</dataInputRefs>
+ </inputSet>
+ <outputSet/>
+ </ioSpecification>
+ <dataInputAssociation>
+ <sourceRef>items</sourceRef>
+ <targetRef>_mi_inColl</targetRef>
+ </dataInputAssociation>
+ <multiInstanceLoopCharacteristics isSequential="false">
+ <loopDataInputRef>_mi_inColl</loopDataInputRef>
+ <inputDataItem xsi:type="tDataInput" id="item" name="item"/>
+ </multiInstanceLoopCharacteristics>
+ </callActivity>
+ <endEvent id="_end" name="End">
+ <terminateEventDefinition/>
+ </endEvent>
+
+ <sequenceFlow id="_start-_mi" sourceRef="_start" targetRef="_mi"/>
+ <sequenceFlow id="_mi-_end" sourceRef="_mi" targetRef="_end"/>
+ </process>
+
+ <bpmndi:BPMNDiagram>
+ <bpmndi:BPMNPlane bpmnElement="MiParentProcess">
+ <bpmndi:BPMNShape bpmnElement="_start">
+ <dc:Bounds x="16" y="16" width="48" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_mi">
+ <dc:Bounds x="96" y="16" width="150" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape bpmnElement="_end">
+ <dc:Bounds x="278" y="16" width="48" height="48"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNEdge bpmnElement="_start-_mi">
+ <di:waypoint x="40" y="40"/>
+ <di:waypoint x="171" y="40"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge bpmnElement="_mi-_end">
+ <di:waypoint x="171" y="40"/>
+ <di:waypoint x="302" y="40"/>
+ </bpmndi:BPMNEdge>
+ </bpmndi:BPMNPlane>
+ </bpmndi:BPMNDiagram>
+
+</definitions>
diff --git
a/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ForEachParallelSubProcessAsyncTest.java
b/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ForEachParallelSubProcessAsyncTest.java
new file mode 100644
index 00000000000..e9aeea8d6ef
--- /dev/null
+++
b/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ForEachParallelSubProcessAsyncTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.jbpm.bpmn2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.jbpm.bpmn2.foreach.MiChildProcessProcess;
+import org.jbpm.bpmn2.foreach.MiParentProcessModel;
+import org.jbpm.bpmn2.foreach.MiParentProcessProcess;
+import org.jbpm.test.utils.ProcessTestHelper;
+import org.junit.jupiter.api.Test;
+import org.kie.api.event.process.ProcessStartedEvent;
+import org.kie.kogito.Application;
+import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener;
+import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
+import org.kie.kogito.process.Process;
+import org.kie.kogito.process.ProcessInstance;
+import org.kie.kogito.process.workitems.impl.DefaultKogitoWorkItemHandler;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests that a parallel multi-instance call activity whose child subprocess
+ * contains an async node (e.g. a {@code customAsync} script task) and
wait-state
+ * user tasks launches all child instances in parallel, rather than falling
back
+ * to sequential execution.
+ *
+ * <p>
+ * Previously, {@code ForEachNodeInstance} fell back to sequential mode when
+ * child instances contained asynchronous nodes.
+ */
+public class ForEachParallelSubProcessAsyncTest {
+
+ @Test
+ public void
testParallelMISubProcessWithAsyncNodeCreatesAllChildInstancesUpFront() {
+ Application app = ProcessTestHelper.newApplication();
+ ProcessTestHelper.registerHandler(app, "Human Task", new
DefaultKogitoWorkItemHandler());
+
+ final List<String> childInstanceIds = new ArrayList<>();
+ ProcessTestHelper.registerProcessEventListener(app, new
DefaultKogitoProcessEventListener() {
+ @Override
+ public void beforeProcessStarted(ProcessStartedEvent event) {
+ if
("MiChildProcess".equals(event.getProcessInstance().getProcessId())) {
+ childInstanceIds.add(((KogitoProcessInstance)
event.getProcessInstance()).getStringId());
+ }
+ }
+ });
+
+ MiChildProcessProcess.newProcess(app);
+ Process<MiParentProcessModel> parentProcess =
MiParentProcessProcess.newProcess(app);
+
+ MiParentProcessModel model = parentProcess.createModel();
+ model.setItems(new ArrayList<>(Arrays.asList(1, 2, 3)));
+
+ ProcessInstance<MiParentProcessModel> parentInstance =
parentProcess.createInstance(model);
+ parentInstance.start();
+
+ // All 3 child instances must have been started in parallel before any
+ // human task is completed. With the old (broken) behaviour the ForEach
+ // would serialise and only 1 child would be active at this point.
+ assertThat(childInstanceIds)
+ .as("All 3 child subprocess instances should be started in
parallel")
+ .hasSize(3);
+
+ assertThat(parentInstance.status())
+ .as("Parent should still be active, waiting for child
subprocesses")
+ .isEqualTo(ProcessInstance.STATE_ACTIVE);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]