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]

Reply via email to