You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by mc...@apache.org on 2026/04/07 13:11:24 UTC

(incubator-kie-kogito-runtimes) branch main updated: [main][incubator-kie-issues-2273] Fix multiple groups expression evaluation… (#4223)

This is an automated email from the ASF dual-hosted git repository.

mcimbalek pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git


The following commit(s) were added to refs/heads/main by this push:
     new 3eb270044c [main][incubator-kie-issues-2273] Fix multiple groups expression evaluation… (#4223)
3eb270044c is described below

commit 3eb270044c06457007dcecbfb98c45553451561c
Author: Martin Cimbalek <ci...@gmail.com>
AuthorDate: Tue Apr 7 15:11:19 2026 +0200

    [main][incubator-kie-issues-2273] Fix multiple groups expression evaluation… (#4223)
    
    * [incubator-kie-issues-2273] Fix multiple groups expression evaluation bug
    * simplify regexes and cleanUp logic
    * Add spring boot integration test
---
 .../org/jbpm/bpmn2/xml/AbstractNodeHandler.java    |  24 +++--
 .../main/java/org/jbpm/util/PatternConstants.java  |   3 +-
 .../manager_multiple_groups_variables.bpmn         | 106 +++++++++++++++++++++
 .../usertask/jpa/it/WsHumanTaskLifeCycleIT.java    |  38 ++++++++
 .../manager_multiple_groups_variables.bpmn         | 106 +++++++++++++++++++++
 .../usertask/jpa/it/WsHumanTaskLifeCycleIT.java    |  40 ++++++++
 6 files changed, 307 insertions(+), 10 deletions(-)

diff --git a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/AbstractNodeHandler.java b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/AbstractNodeHandler.java
index 91f850e25b..05149b40cb 100755
--- a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/AbstractNodeHandler.java
+++ b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/AbstractNodeHandler.java
@@ -636,17 +636,23 @@ public abstract class AbstractNodeHandler extends BaseAbstractHandler implements
         return assignments;
     }
 
+    /**
+     * Simplifies variable expression in order to improve performance
+     *
+     * If the expression contains just once variable, we can skip MVEL expression evaluation
+     * 
+     * @param expression MVEL expression to evaluate
+     * @return Variable name if expression evaluation is not needed, original expression otherwise
+     */
     private String cleanUp(String expression) {
-        // this is for user task expressions (they should be set as it is)
-        if (expression.startsWith("[")) {
-            return expression;
-        }
-        Matcher matcher = PatternConstants.PARAMETER_MATCHER.matcher(expression);
-        String temp = expression;
-        if (matcher.find()) {
-            temp = matcher.group(1);
+        Matcher matcher = PatternConstants.SINGLE_PARAMETER_MATCHER.matcher(expression);
+        if (matcher.matches()) {
+            if (!matcher.group(1).contains(".")) {
+                return matcher.group(1);
+            }
         }
-        return temp.contains(".") ? expression : temp;
+
+        return expression;
     }
 
     private DataDefinition toDataExpression(String id, String expression) {
diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/util/PatternConstants.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/util/PatternConstants.java
index 1f7b478040..af99e19f1e 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/util/PatternConstants.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/util/PatternConstants.java
@@ -22,7 +22,8 @@ import java.util.regex.Pattern;
 
 public class PatternConstants {
 
-    public static final Pattern PARAMETER_MATCHER = Pattern.compile("#\\{([\\S|\\p{javaWhitespace}&&[^\\}]]+)\\}", Pattern.DOTALL);
+    public static final Pattern PARAMETER_MATCHER = Pattern.compile("#\\{([^}]+)\\}", Pattern.DOTALL);
+    public static final Pattern SINGLE_PARAMETER_MATCHER = Pattern.compile("#\\{([^}]+)\\}$");
     public static final Pattern SIMPLE_TIME_DATE_MATCHER = Pattern.compile("([+-])?\\s*((\\d+)[Ww])?\\s*((\\d+)[Dd])?\\s*((\\d+)[Hh])?\\s*((\\d+)[Mm])?\\s*((\\d+)[Ss])?");
 
     private PatternConstants() {
diff --git a/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/main/resources/manager_multiple_groups_variables.bpmn b/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/main/resources/manager_multiple_groups_variables.bpmn
new file mode 100644
index 0000000000..a31c5feb81
--- /dev/null
+++ b/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/main/resources/manager_multiple_groups_variables.bpmn
@@ -0,0 +1,106 @@
+<?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.
+  -->
+<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:bpsim="http://www.bpsim.org/schemas/1.0" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:drools="http://www.jboss.org/drools" id="_multiGroupVarTest" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd http://www.jboss [...]
+  <bpmn2:itemDefinition id="__MGV_Task_SkippableInputXItem" structureRef="Object"/>
+  <bpmn2:itemDefinition id="__MGV_Task_TaskNameInputXItem" structureRef="Object"/>
+  <bpmn2:itemDefinition id="__MGV_Task_GroupIdInputXItem" structureRef="Object"/>
+  <bpmn2:itemDefinition id="_group1Item" structureRef="String"/>
+  <bpmn2:itemDefinition id="_group2Item" structureRef="String"/>
+  <bpmn2:itemDefinition id="_group3Item" structureRef="String"/>
+  <bpmn2:collaboration id="_MGV_Collaboration" name="Default Collaboration">
+    <bpmn2:participant id="_MGV_Participant" name="Pool Participant" processRef="manager_multiple_groups_variables"/>
+  </bpmn2:collaboration>
+  <bpmn2:process id="manager_multiple_groups_variables" drools:packageName="com.example" drools:version="1.0" drools:adHoc="false" name="manager_multiple_groups_variables" isExecutable="true" processType="Public">
+    <bpmn2:property id="group1" itemSubjectRef="_group1Item" name="group1"/>
+    <bpmn2:property id="group2" itemSubjectRef="_group2Item" name="group2"/>
+    <bpmn2:property id="group3" itemSubjectRef="_group3Item" name="group3"/>
+    <bpmn2:sequenceFlow id="_MGV_Flow2" sourceRef="_MGV_Task" targetRef="_MGV_End"/>
+    <bpmn2:sequenceFlow id="_MGV_Flow1" sourceRef="_MGV_Start" targetRef="_MGV_Task"/>
+    <bpmn2:endEvent id="_MGV_End">
+      <bpmn2:incoming>_MGV_Flow2</bpmn2:incoming>
+    </bpmn2:endEvent>
+    <bpmn2:userTask id="_MGV_Task" name="Task">
+      <bpmn2:extensionElements>
+        <drools:metaData name="elementname">
+          <drools:metaValue><![CDATA[Task]]></drools:metaValue>
+        </drools:metaData>
+      </bpmn2:extensionElements>
+      <bpmn2:incoming>_MGV_Flow1</bpmn2:incoming>
+      <bpmn2:outgoing>_MGV_Flow2</bpmn2:outgoing>
+      <bpmn2:ioSpecification>
+        <bpmn2:dataInput id="_MGV_Task_TaskNameInputX" drools:dtype="Object" itemSubjectRef="__MGV_Task_TaskNameInputXItem" name="TaskName"/>
+        <bpmn2:dataInput id="_MGV_Task_SkippableInputX" drools:dtype="Object" itemSubjectRef="__MGV_Task_SkippableInputXItem" name="Skippable"/>
+        <bpmn2:dataInput id="_MGV_Task_GroupIdInputX" drools:dtype="Object" itemSubjectRef="__MGV_Task_GroupIdInputXItem" name="GroupId"/>
+        <bpmn2:inputSet>
+          <bpmn2:dataInputRefs>_MGV_Task_TaskNameInputX</bpmn2:dataInputRefs>
+          <bpmn2:dataInputRefs>_MGV_Task_SkippableInputX</bpmn2:dataInputRefs>
+          <bpmn2:dataInputRefs>_MGV_Task_GroupIdInputX</bpmn2:dataInputRefs>
+        </bpmn2:inputSet>
+      </bpmn2:ioSpecification>
+      <bpmn2:dataInputAssociation>
+        <bpmn2:targetRef>_MGV_Task_TaskNameInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[Task]]></bpmn2:from>
+          <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_MGV_Task_TaskNameInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+      <bpmn2:dataInputAssociation>
+        <bpmn2:targetRef>_MGV_Task_SkippableInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[false]]></bpmn2:from>
+          <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_MGV_Task_SkippableInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+      <bpmn2:dataInputAssociation>
+        <bpmn2:targetRef>_MGV_Task_GroupIdInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[#{group1},#{group2},#{group3}]]></bpmn2:from>
+          <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_MGV_Task_GroupIdInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+    </bpmn2:userTask>
+    <bpmn2:startEvent id="_MGV_Start">
+      <bpmn2:outgoing>_MGV_Flow1</bpmn2:outgoing>
+    </bpmn2:startEvent>
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram>
+    <bpmndi:BPMNPlane bpmnElement="manager_multiple_groups_variables">
+      <bpmndi:BPMNShape id="shape__MGV_Start" bpmnElement="_MGV_Start">
+        <dc:Bounds height="56" width="56" x="100" y="100"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__MGV_Task" bpmnElement="_MGV_Task">
+        <dc:Bounds height="102" width="154" x="236" y="77"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__MGV_End" bpmnElement="_MGV_End">
+        <dc:Bounds height="56" width="56" x="470" y="100"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="edge_shape__MGV_Start_to_shape__MGV_Task" bpmnElement="_MGV_Flow1">
+        <di:waypoint x="128" y="128"/>
+        <di:waypoint x="236" y="128"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="edge_shape__MGV_Task_to_shape__MGV_End" bpmnElement="_MGV_Flow2">
+        <di:waypoint x="313" y="128"/>
+        <di:waypoint x="498" y="128"/>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn2:definitions>
+
+<!-- Made with Bob -->
diff --git a/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java b/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java
index 8b2a087221..0994d4a692 100644
--- a/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java
+++ b/quarkus/integration-tests/integration-tests-quarkus-wshumantasks/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java
@@ -622,6 +622,44 @@ public class WsHumanTaskLifeCycleIT {
         isProcessCompleted(processId, pid);
     }
 
+    @Test
+    public void testMultipleGroupsWithVariablesUserTaskLifeCycle() {
+        // This test verifies the fix for multi-variable expressions in BPMN
+        // The GroupId is defined as #{group1},#{group2},#{group3} in the BPMN
+        var user = "dave";
+        var group = "engineering";
+        var potentialGroups = new String[] { "hr", "engineering", "management" };
+        var processId = "manager_multiple_groups_variables";
+
+        // Start process with variables that will be resolved in GroupId expression
+        var pid = startProcessInstanceWithVariables(processId, Map.of(
+                "group1", "hr",
+                "group2", "engineering",
+                "group3", "management"));
+
+        var taskId = getTaskId(user, group, pid);
+
+        // Verify all three groups are present in potentialGroups
+        given()
+                .contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", user)
+                .queryParam("group", group)
+                .get(USER_TASKS_INSTANCE_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .body("id", equalTo(taskId))
+                .body("status.name", equalTo("Ready"))
+                .body("potentialUsers", equalTo(List.of()))
+                .body("potentialGroups", hasItems(potentialGroups));
+
+        claim(taskId, user, group);
+        start(taskId, user, group);
+        complete(taskId, user, group);
+
+        isProcessCompleted(processId, pid);
+    }
+
     private void nominate(String taskId, String user, String status, String[] nominatedUsers) {
         given()
                 .contentType(ContentType.JSON)
diff --git a/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/main/resources/manager_multiple_groups_variables.bpmn b/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/main/resources/manager_multiple_groups_variables.bpmn
new file mode 100644
index 0000000000..a31c5feb81
--- /dev/null
+++ b/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/main/resources/manager_multiple_groups_variables.bpmn
@@ -0,0 +1,106 @@
+<?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.
+  -->
+<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:bpsim="http://www.bpsim.org/schemas/1.0" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:drools="http://www.jboss.org/drools" id="_multiGroupVarTest" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd http://www.jboss [...]
+  <bpmn2:itemDefinition id="__MGV_Task_SkippableInputXItem" structureRef="Object"/>
+  <bpmn2:itemDefinition id="__MGV_Task_TaskNameInputXItem" structureRef="Object"/>
+  <bpmn2:itemDefinition id="__MGV_Task_GroupIdInputXItem" structureRef="Object"/>
+  <bpmn2:itemDefinition id="_group1Item" structureRef="String"/>
+  <bpmn2:itemDefinition id="_group2Item" structureRef="String"/>
+  <bpmn2:itemDefinition id="_group3Item" structureRef="String"/>
+  <bpmn2:collaboration id="_MGV_Collaboration" name="Default Collaboration">
+    <bpmn2:participant id="_MGV_Participant" name="Pool Participant" processRef="manager_multiple_groups_variables"/>
+  </bpmn2:collaboration>
+  <bpmn2:process id="manager_multiple_groups_variables" drools:packageName="com.example" drools:version="1.0" drools:adHoc="false" name="manager_multiple_groups_variables" isExecutable="true" processType="Public">
+    <bpmn2:property id="group1" itemSubjectRef="_group1Item" name="group1"/>
+    <bpmn2:property id="group2" itemSubjectRef="_group2Item" name="group2"/>
+    <bpmn2:property id="group3" itemSubjectRef="_group3Item" name="group3"/>
+    <bpmn2:sequenceFlow id="_MGV_Flow2" sourceRef="_MGV_Task" targetRef="_MGV_End"/>
+    <bpmn2:sequenceFlow id="_MGV_Flow1" sourceRef="_MGV_Start" targetRef="_MGV_Task"/>
+    <bpmn2:endEvent id="_MGV_End">
+      <bpmn2:incoming>_MGV_Flow2</bpmn2:incoming>
+    </bpmn2:endEvent>
+    <bpmn2:userTask id="_MGV_Task" name="Task">
+      <bpmn2:extensionElements>
+        <drools:metaData name="elementname">
+          <drools:metaValue><![CDATA[Task]]></drools:metaValue>
+        </drools:metaData>
+      </bpmn2:extensionElements>
+      <bpmn2:incoming>_MGV_Flow1</bpmn2:incoming>
+      <bpmn2:outgoing>_MGV_Flow2</bpmn2:outgoing>
+      <bpmn2:ioSpecification>
+        <bpmn2:dataInput id="_MGV_Task_TaskNameInputX" drools:dtype="Object" itemSubjectRef="__MGV_Task_TaskNameInputXItem" name="TaskName"/>
+        <bpmn2:dataInput id="_MGV_Task_SkippableInputX" drools:dtype="Object" itemSubjectRef="__MGV_Task_SkippableInputXItem" name="Skippable"/>
+        <bpmn2:dataInput id="_MGV_Task_GroupIdInputX" drools:dtype="Object" itemSubjectRef="__MGV_Task_GroupIdInputXItem" name="GroupId"/>
+        <bpmn2:inputSet>
+          <bpmn2:dataInputRefs>_MGV_Task_TaskNameInputX</bpmn2:dataInputRefs>
+          <bpmn2:dataInputRefs>_MGV_Task_SkippableInputX</bpmn2:dataInputRefs>
+          <bpmn2:dataInputRefs>_MGV_Task_GroupIdInputX</bpmn2:dataInputRefs>
+        </bpmn2:inputSet>
+      </bpmn2:ioSpecification>
+      <bpmn2:dataInputAssociation>
+        <bpmn2:targetRef>_MGV_Task_TaskNameInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[Task]]></bpmn2:from>
+          <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_MGV_Task_TaskNameInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+      <bpmn2:dataInputAssociation>
+        <bpmn2:targetRef>_MGV_Task_SkippableInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[false]]></bpmn2:from>
+          <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_MGV_Task_SkippableInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+      <bpmn2:dataInputAssociation>
+        <bpmn2:targetRef>_MGV_Task_GroupIdInputX</bpmn2:targetRef>
+        <bpmn2:assignment>
+          <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[#{group1},#{group2},#{group3}]]></bpmn2:from>
+          <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_MGV_Task_GroupIdInputX]]></bpmn2:to>
+        </bpmn2:assignment>
+      </bpmn2:dataInputAssociation>
+    </bpmn2:userTask>
+    <bpmn2:startEvent id="_MGV_Start">
+      <bpmn2:outgoing>_MGV_Flow1</bpmn2:outgoing>
+    </bpmn2:startEvent>
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram>
+    <bpmndi:BPMNPlane bpmnElement="manager_multiple_groups_variables">
+      <bpmndi:BPMNShape id="shape__MGV_Start" bpmnElement="_MGV_Start">
+        <dc:Bounds height="56" width="56" x="100" y="100"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__MGV_Task" bpmnElement="_MGV_Task">
+        <dc:Bounds height="102" width="154" x="236" y="77"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__MGV_End" bpmnElement="_MGV_End">
+        <dc:Bounds height="56" width="56" x="470" y="100"/>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="edge_shape__MGV_Start_to_shape__MGV_Task" bpmnElement="_MGV_Flow1">
+        <di:waypoint x="128" y="128"/>
+        <di:waypoint x="236" y="128"/>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="edge_shape__MGV_Task_to_shape__MGV_End" bpmnElement="_MGV_Flow2">
+        <di:waypoint x="313" y="128"/>
+        <di:waypoint x="498" y="128"/>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn2:definitions>
+
+<!-- Made with Bob -->
diff --git a/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java b/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java
index 53187afdfc..2f811265a2 100644
--- a/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java
+++ b/springboot/integration-tests/integration-tests-springboot-wshumantasks-it/src/test/java/org/jbpm/usertask/jpa/it/WsHumanTaskLifeCycleIT.java
@@ -631,6 +631,46 @@ public class WsHumanTaskLifeCycleIT {
         isProcessCompleted(processId, pid);
     }
 
+    @Test
+    public void testMultipleGroupsWithVariablesUserTaskLifeCycle() {
+        // This test verifies the fix for multi-variable expressions in BPMN
+        // The GroupId is defined as #{group1},#{group2},#{group3} in the BPMN
+        // and we pass the actual group names as process variables
+        var user = "dave";
+        var group = "engineering";
+        var potentialGroups = new String[] { "hr", "engineering", "management" };
+        var processId = "manager_multiple_groups_variables";
+
+        // Start process with variables that will be resolved in GroupId expression
+        var pid = startProcessInstanceWithVariables(processId, Map.of(
+                "group1", "hr",
+                "group2", "engineering",
+                "group3", "management"));
+
+        var taskId = getTaskId(user, group, pid);
+
+        // Verify all three groups are present in potentialGroups
+        // This would fail with the old implementation which only extracted the first variable
+        given()
+                .contentType(ContentType.JSON)
+                .when()
+                .queryParam("user", user)
+                .queryParam("group", group)
+                .get(USER_TASKS_INSTANCE_ENDPOINT, taskId)
+                .then()
+                .statusCode(200)
+                .body("id", equalTo(taskId))
+                .body("status.name", equalTo("Ready"))
+                .body("potentialUsers", equalTo(List.of()))
+                .body("potentialGroups", hasItems(potentialGroups));
+
+        claim(taskId, user, group);
+        start(taskId, user, group);
+        complete(taskId, user, group);
+
+        isProcessCompleted(processId, pid);
+    }
+
     private void nominate(String taskId, String user, String status, String[] nominatedUsers) {
         given()
                 .contentType(ContentType.JSON)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org