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

fjtiradosarti 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 73b0e84fa5 [Fix #3456]Support multiple constraint over same connection 
(#3459)
73b0e84fa5 is described below

commit 73b0e84fa5cbe42e71dc51059a757ffe6362f017
Author: Francisco Javier Tirado Sarti 
<[email protected]>
AuthorDate: Wed Apr 3 16:49:32 2024 +0200

    [Fix #3456]Support multiple constraint over same connection (#3459)
    
    * [Fix #3456]Support multiple constraint over same connection
    
    * [Fix #3456] Unit test
    
    * [Fix #3456] Gonzalo's comments
---
 .../org/jbpm/bpmn2/feel/FeelProcessValidator.java  | 21 ++++---
 .../main/java/org/jbpm/bpmn2/xml/SplitHandler.java | 33 ++++++-----
 .../java/org/jbpm/compiler/ProcessBuilderImpl.java | 22 ++++---
 .../jbpm/compiler/canonical/SplitNodeVisitor.java  | 59 +++++++++---------
 .../jbpm/compiler/canonical/StateNodeVisitor.java  | 30 ++++++----
 .../MultiConditionalSequenceFlowNodeBuilder.java   | 57 +++++++++---------
 .../org/jbpm/process/builder/SplitNodeBuilder.java | 68 +++++++++++----------
 .../core/validation/RuleFlowProcessValidator.java  |  8 +--
 .../java/org/jbpm/workflow/core/impl/NodeImpl.java | 20 +++++--
 .../java/org/jbpm/workflow/core/node/Split.java    | 41 ++++---------
 .../org/jbpm/workflow/core/node/StateNode.java     | 53 -----------------
 .../workflow/instance/impl/NodeInstanceImpl.java   | 36 +++++++----
 .../jbpm/workflow/instance/node/SplitInstance.java | 69 ++++++++++++++--------
 .../workflow/instance/node/StateNodeInstance.java  | 50 ++++++++++------
 .../codegen/process/ProcessGenerationIT.java       | 16 ++---
 .../workflow/ServerlessWorkflowParsingTest.java    | 17 ++----
 .../serverless/workflow/WorkflowTestUtils.java     |  2 +
 .../executor/StaticWorkflowApplicationTest.java    | 15 +++++
 .../src/test/resources/switch.yaml                 | 32 ++++++++++
 19 files changed, 355 insertions(+), 294 deletions(-)

diff --git 
a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/feel/FeelProcessValidator.java 
b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/feel/FeelProcessValidator.java
index 5ff61b89ae..ccf60a5a4c 100755
--- 
a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/feel/FeelProcessValidator.java
+++ 
b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/feel/FeelProcessValidator.java
@@ -19,6 +19,7 @@
 package org.jbpm.bpmn2.feel;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -63,15 +64,17 @@ public class FeelProcessValidator extends 
RuleFlowProcessValidator {
         Arrays.stream(nodes).filter(n -> n instanceof Split).forEach(node -> {
             final Split split = (Split) node;
             if (split.getType() == Split.TYPE_XOR || split.getType() == 
Split.TYPE_OR) {
-                for (Map.Entry<ConnectionRef, Constraint> entry : 
split.getConstraints().entrySet()) {
-                    if (entry.getValue() != null && 
"FEEL".equals(entry.getValue().getDialect())) {
-                        try {
-                            
verifyFEELbyCompilingExpression(process.getVariableScope(), 
entry.getValue().getConstraint());
-                        } catch (FeelCompilationException ex) {
-                            addErrorMessage(process,
-                                    node,
-                                    errors,
-                                    format("Invalid FEEL expression: '%s'.", 
entry.getValue().getConstraint()));
+                for (Map.Entry<ConnectionRef, Collection<Constraint>> entry : 
split.getConstraints().entrySet()) {
+                    for (Constraint constraint : entry.getValue()) {
+                        if (constraint != null && 
"FEEL".equals(constraint.getDialect())) {
+                            try {
+                                
verifyFEELbyCompilingExpression(process.getVariableScope(), 
constraint.getConstraint());
+                            } catch (FeelCompilationException ex) {
+                                addErrorMessage(process,
+                                        node,
+                                        errors,
+                                        format("Invalid FEEL expression: 
'%s'.", constraint.getConstraint()));
+                            }
                         }
                     }
                 }
diff --git a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/SplitHandler.java 
b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/SplitHandler.java
index 760172f8a5..8c908b508e 100755
--- a/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/SplitHandler.java
+++ b/jbpm/jbpm-bpmn2/src/main/java/org/jbpm/bpmn2/xml/SplitHandler.java
@@ -18,6 +18,7 @@
  */
 package org.jbpm.bpmn2.xml;
 
+import java.util.Collection;
 import java.util.Map;
 
 import org.jbpm.workflow.core.Constraint;
@@ -48,26 +49,30 @@ public class SplitHandler extends AbstractNodeHandler {
             case Split.TYPE_XOR:
                 type = "exclusiveGateway";
                 writeNode(type, node, xmlDump, metaDataType);
-                for (Map.Entry<ConnectionRef, Constraint> entry : 
split.getConstraints().entrySet()) {
-                    if (entry.getValue() != null && 
entry.getValue().isDefault()) {
-                        xmlDump.append("default=\"" +
-                                XmlBPMNProcessDumper.getUniqueNodeId(split) + 
"-" +
-                                
XmlBPMNProcessDumper.getUniqueNodeId(node.getParentContainer().getNode(entry.getKey().getNodeId()))
 +
-                                "\" ");
-                        break;
+                for (Map.Entry<ConnectionRef, Collection<Constraint>> entry : 
split.getConstraints().entrySet()) {
+                    for (Constraint constraint : entry.getValue()) {
+                        if (constraint != null && constraint.isDefault()) {
+                            xmlDump.append("default=\"" +
+                                    
XmlBPMNProcessDumper.getUniqueNodeId(split) + "-" +
+                                    
XmlBPMNProcessDumper.getUniqueNodeId(node.getParentContainer().getNode(entry.getKey().getNodeId()))
 +
+                                    "\" ");
+                            break;
+                        }
                     }
                 }
                 break;
             case Split.TYPE_OR:
                 type = "inclusiveGateway";
                 writeNode(type, node, xmlDump, metaDataType);
-                for (Map.Entry<ConnectionRef, Constraint> entry : 
split.getConstraints().entrySet()) {
-                    if (entry.getValue() != null && 
entry.getValue().isDefault()) {
-                        xmlDump.append("default=\"" +
-                                XmlBPMNProcessDumper.getUniqueNodeId(split) + 
"-" +
-                                
XmlBPMNProcessDumper.getUniqueNodeId(node.getParentContainer().getNode(entry.getKey().getNodeId()))
 +
-                                "\" ");
-                        break;
+                for (Map.Entry<ConnectionRef, Collection<Constraint>> entry : 
split.getConstraints().entrySet()) {
+                    for (Constraint constraint : entry.getValue()) {
+                        if (constraint != null && constraint.isDefault()) {
+                            xmlDump.append("default=\"" +
+                                    
XmlBPMNProcessDumper.getUniqueNodeId(split) + "-" +
+                                    
XmlBPMNProcessDumper.getUniqueNodeId(node.getParentContainer().getNode(entry.getKey().getNodeId()))
 +
+                                    "\" ");
+                            break;
+                        }
                     }
                 }
                 break;
diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/ProcessBuilderImpl.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/ProcessBuilderImpl.java
index 9e9dd2bf92..3257a0db6f 100755
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/ProcessBuilderImpl.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/ProcessBuilderImpl.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -330,11 +331,14 @@ public class ProcessBuilderImpl implements 
org.drools.compiler.compiler.ProcessB
                 Split split = (Split) nodes[i];
                 if (split.getType() == Split.TYPE_XOR || split.getType() == 
Split.TYPE_OR) {
                     for (Connection connection : 
split.getDefaultOutgoingConnections()) {
-                        Constraint constraint = 
split.getConstraint(connection);
-                        if (constraint != null && 
"rule".equals(constraint.getType())) {
-                            builder.append(createSplitRule(process,
-                                    connection,
-                                    
split.getConstraint(connection).getConstraint()));
+                        Collection<Constraint> constraints = 
split.getConstraints(connection);
+                        if (constraints != null) {
+                            for (Constraint constraint : constraints)
+                                if (constraint != null && 
"rule".equals(constraint.getType())) {
+                                    builder.append(createSplitRule(process,
+                                            connection,
+                                            constraint.getConstraint()));
+                                }
                         }
                     }
                 }
@@ -385,7 +389,7 @@ public class ProcessBuilderImpl implements 
org.drools.compiler.compiler.ProcessB
                     key.getNodeId() + "-" + key.getToType() + "\" 
@Propagation(EAGER) \n" +
                     "      ruleflow-group \"DROOLS_SYSTEM\" \n" +
                     "    when \n" +
-                    "      " + state.getConstraints().get(key).getConstraint() 
+ "\n" +
+                    "      " + 
state.internalGetConstraint(key).getConstraint() + "\n" +
                     "    then \n" +
                     "end \n\n";
         }
@@ -426,8 +430,10 @@ public class ProcessBuilderImpl implements 
org.drools.compiler.compiler.ProcessB
 
     private String createStateRules(Process process, StateNode state) {
         StringBuilder result = new StringBuilder();
-        for (Map.Entry<ConnectionRef, Constraint> entry : 
state.getConstraints().entrySet()) {
-            result.append(createStateRule(process, state, entry.getKey(), 
entry.getValue()));
+        for (Map.Entry<ConnectionRef, Collection<Constraint>> entry : 
state.getConstraints().entrySet()) {
+            for (Constraint constraint : entry.getValue()) {
+                result.append(createStateRule(process, state, entry.getKey(), 
constraint));
+            }
         }
         return result.toString();
     }
diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/SplitNodeVisitor.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/SplitNodeVisitor.java
index b60a5f3bfb..4eebbc0a16 100644
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/SplitNodeVisitor.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/SplitNodeVisitor.java
@@ -18,6 +18,7 @@
  */
 package org.jbpm.compiler.canonical;
 
+import java.util.Collection;
 import java.util.Map.Entry;
 import java.util.function.Supplier;
 
@@ -61,45 +62,49 @@ public class SplitNodeVisitor extends 
AbstractNodeVisitor<Split> {
         visitMetaData(node.getMetaData(), body, getNodeId(node));
 
         if (node.getType() == Split.TYPE_OR || node.getType() == 
Split.TYPE_XOR) {
-            for (Entry<ConnectionRef, Constraint> entry : 
node.getConstraints().entrySet()) {
+            for (Entry<ConnectionRef, Collection<Constraint>> entry : 
node.getConstraints().entrySet()) {
                 if (entry.getValue() != null) {
-                    Expression returnValueEvaluator;
-                    if (entry.getValue() instanceof 
ReturnValueConstraintEvaluator && ((ReturnValueConstraintEvaluator) 
entry.getValue()).getReturnValueEvaluator() instanceof Supplier) {
-                        returnValueEvaluator = ((Supplier<Expression>) 
((ReturnValueConstraintEvaluator) 
entry.getValue()).getReturnValueEvaluator()).get();
-                    } else if ("FEEL".equals(entry.getValue().getDialect())) {
-                        returnValueEvaluator = 
buildFEELReturnValueEvaluator(entry);
-                    } else {
-                        BlockStmt actionBody = new BlockStmt();
-                        LambdaExpr lambda = new LambdaExpr(
-                                new Parameter(new UnknownType(), 
KCONTEXT_VAR), // (kcontext) ->
-                                actionBody);
+                    for (Constraint constraint : entry.getValue()) {
+                        if (constraint != null) {
+                            Expression returnValueEvaluator;
+                            if (constraint instanceof 
ReturnValueConstraintEvaluator && ((ReturnValueConstraintEvaluator) 
constraint).getReturnValueEvaluator() instanceof Supplier) {
+                                returnValueEvaluator = ((Supplier<Expression>) 
((ReturnValueConstraintEvaluator) constraint).getReturnValueEvaluator()).get();
+                            } else if ("FEEL".equals(constraint.getDialect())) 
{
+                                returnValueEvaluator = 
buildFEELReturnValueEvaluator(constraint);
+                            } else {
+                                BlockStmt actionBody = new BlockStmt();
+                                LambdaExpr lambda = new LambdaExpr(
+                                        new Parameter(new UnknownType(), 
KCONTEXT_VAR), // (kcontext) ->
+                                        actionBody);
 
-                        for (Variable v : variableScope.getVariables()) {
-                            actionBody.addStatement(makeAssignment(v));
-                        }
+                                for (Variable v : 
variableScope.getVariables()) {
+                                    actionBody.addStatement(makeAssignment(v));
+                                }
 
-                        BlockStmt blockStmt = StaticJavaParser.parseBlock("{" 
+ entry.getValue().getConstraint() + "}");
-                        
blockStmt.getStatements().forEach(actionBody::addStatement);
+                                BlockStmt blockStmt = 
StaticJavaParser.parseBlock("{" + constraint.getConstraint() + "}");
+                                
blockStmt.getStatements().forEach(actionBody::addStatement);
 
-                        returnValueEvaluator = lambda;
+                                returnValueEvaluator = lambda;
+                            }
+                            
body.addStatement(getFactoryMethod(getNodeId(node), METHOD_CONSTRAINT,
+                                    new 
LongLiteralExpr(entry.getKey().getNodeId()),
+                                    new 
StringLiteralExpr(getOrDefault(entry.getKey().getConnectionId(), "")),
+                                    new 
StringLiteralExpr(entry.getKey().getToType()),
+                                    new 
StringLiteralExpr(constraint.getDialect()),
+                                    returnValueEvaluator,
+                                    new 
IntegerLiteralExpr(constraint.getPriority()),
+                                    new 
BooleanLiteralExpr(constraint.isDefault())));
+                        }
                     }
-                    body.addStatement(getFactoryMethod(getNodeId(node), 
METHOD_CONSTRAINT,
-                            new LongLiteralExpr(entry.getKey().getNodeId()),
-                            new 
StringLiteralExpr(getOrDefault(entry.getKey().getConnectionId(), "")),
-                            new StringLiteralExpr(entry.getKey().getToType()),
-                            new 
StringLiteralExpr(entry.getValue().getDialect()),
-                            returnValueEvaluator,
-                            new 
IntegerLiteralExpr(entry.getValue().getPriority()),
-                            new 
BooleanLiteralExpr(entry.getValue().isDefault())));
                 }
             }
         }
         body.addStatement(getDoneMethod(getNodeId(node)));
     }
 
-    public static ObjectCreationExpr 
buildFEELReturnValueEvaluator(Entry<ConnectionRef, Constraint> entry) {
+    private static ObjectCreationExpr buildFEELReturnValueEvaluator(Constraint 
constraint) {
         return new ObjectCreationExpr(null,
                 
StaticJavaParser.parseClassOrInterfaceType("org.jbpm.bpmn2.feel.FeelReturnValueEvaluator"),
-                new NodeList<>(new 
StringLiteralExpr(entry.getValue().getConstraint())));
+                new NodeList<>(new 
StringLiteralExpr(constraint.getConstraint())));
     }
 }
diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/StateNodeVisitor.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/StateNodeVisitor.java
index 31faf6b45c..cec7b37a91 100644
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/StateNodeVisitor.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/StateNodeVisitor.java
@@ -18,11 +18,15 @@
  */
 package org.jbpm.compiler.canonical;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Map;
 import java.util.stream.Stream;
 
 import org.jbpm.process.core.context.variable.VariableScope;
 import org.jbpm.ruleflow.core.factory.StateNodeFactory;
+import org.jbpm.workflow.core.Constraint;
+import org.jbpm.workflow.core.impl.ConnectionRef;
 import org.jbpm.workflow.core.node.StateNode;
 import org.kie.api.definition.process.Node;
 
@@ -60,15 +64,21 @@ public class StateNodeVisitor extends 
CompositeContextNodeVisitor<StateNode> {
         if (node.getConstraints() == null) {
             return Stream.empty();
         }
-        return node.getConstraints()
-                .entrySet()
-                .stream()
-                .map((e -> getFactoryMethod(getNodeId(node), METHOD_CONSTRAINT,
-                        getOrNullExpr(e.getKey().getConnectionId()),
-                        new LongLiteralExpr(e.getKey().getNodeId()),
-                        new StringLiteralExpr(e.getKey().getToType()),
-                        new StringLiteralExpr(e.getValue().getDialect()),
-                        new 
StringLiteralExpr(StringEscapeUtils.escapeJava(e.getValue().getConstraint())),
-                        new IntegerLiteralExpr(e.getValue().getPriority()))));
+        Collection<MethodCallExpr> result = new ArrayList<>();
+        for (Map.Entry<ConnectionRef, Collection<Constraint>> entry : 
node.getConstraints().entrySet()) {
+            ConnectionRef ref = entry.getKey();
+            for (Constraint constraint : entry.getValue()) {
+                if (constraint != null) {
+                    result.add(getFactoryMethod(getNodeId(node), 
METHOD_CONSTRAINT,
+                            getOrNullExpr(ref.getConnectionId()),
+                            new LongLiteralExpr(ref.getNodeId()),
+                            new StringLiteralExpr(ref.getToType()),
+                            new StringLiteralExpr(constraint.getDialect()),
+                            new 
StringLiteralExpr(StringEscapeUtils.escapeJava(constraint.getConstraint())),
+                            new IntegerLiteralExpr(constraint.getPriority())));
+                }
+            }
+        }
+        return result.stream();
     }
 }
diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/MultiConditionalSequenceFlowNodeBuilder.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/MultiConditionalSequenceFlowNodeBuilder.java
index 917e1f3ccb..5f6f92f395 100755
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/MultiConditionalSequenceFlowNodeBuilder.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/MultiConditionalSequenceFlowNodeBuilder.java
@@ -18,8 +18,9 @@
  */
 package org.jbpm.process.builder;
 
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.Map;
 
 import org.drools.drl.ast.descr.ProcessDescr;
@@ -30,7 +31,6 @@ import 
org.jbpm.process.instance.impl.ReturnValueConstraintEvaluator;
 import org.jbpm.process.instance.impl.RuleConstraintEvaluator;
 import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.impl.ConnectionRef;
-import org.jbpm.workflow.core.impl.ConstraintImpl;
 import org.jbpm.workflow.core.impl.NodeImpl;
 import org.jbpm.workflow.core.node.Split;
 import org.kie.api.definition.process.Connection;
@@ -42,19 +42,16 @@ public class MultiConditionalSequenceFlowNodeBuilder 
implements ProcessNodeBuild
     public void build(Process process, ProcessDescr processDescr,
             ProcessBuildContext context, Node node) {
 
-        Map<ConnectionRef, Constraint> constraints = ((NodeImpl) 
node).getConstraints();
-
         // exclude split as it is handled with separate builder and nodes with 
non conditional sequence flows
-        if (node instanceof Split || constraints.size() == 0) {
+        if (node instanceof Split) {
             return;
         }
 
         // we need to clone the map, so we can update the original while 
iterating.
-        Map<ConnectionRef, Constraint> map = new HashMap<>(constraints);
-        for (Iterator<Map.Entry<ConnectionRef, Constraint>> it = 
map.entrySet().iterator(); it.hasNext();) {
-            Map.Entry<ConnectionRef, Constraint> entry = it.next();
+        for (Iterator<Map.Entry<ConnectionRef, Collection<Constraint>>> it = 
((NodeImpl) node).getConstraints().entrySet().iterator(); it.hasNext();) {
+            Map.Entry<ConnectionRef, Collection<Constraint>> entry = it.next();
             ConnectionRef connection = entry.getKey();
-            ConstraintImpl constraint = (ConstraintImpl) entry.getValue();
+            Collection<Constraint> constraints = new 
LinkedList<>(entry.getValue());
             Connection outgoingConnection = null;
             for (Connection out : ((NodeImpl) 
node).getDefaultOutgoingConnections()) {
                 if (out.getToType().equals(connection.getToType())
@@ -65,27 +62,31 @@ public class MultiConditionalSequenceFlowNodeBuilder 
implements ProcessNodeBuild
             if (outgoingConnection == null) {
                 throw new IllegalArgumentException("Could not find outgoing 
connection");
             }
-            if ("rule".equals(constraint.getType())) {
-                RuleConstraintEvaluator ruleConstraint = new 
RuleConstraintEvaluator();
-                ruleConstraint.setDialect(constraint.getDialect());
-                ruleConstraint.setName(constraint.getName());
-                ruleConstraint.setPriority(constraint.getPriority());
-                ruleConstraint.setDefault(constraint.isDefault());
-                ((NodeImpl) node).setConstraint(outgoingConnection, 
ruleConstraint);
-            } else if ("code".equals(constraint.getType())) {
-                ReturnValueConstraintEvaluator returnValueConstraint = new 
ReturnValueConstraintEvaluator();
-                returnValueConstraint.setDialect(constraint.getDialect());
-                returnValueConstraint.setName(constraint.getName());
-                returnValueConstraint.setPriority(constraint.getPriority());
-                returnValueConstraint.setDefault(constraint.isDefault());
-                ((NodeImpl) node).setConstraint(outgoingConnection, 
returnValueConstraint);
+            for (Constraint constraint : constraints) {
+                if (constraint != null) {
+                    if ("rule".equals(constraint.getType())) {
+                        RuleConstraintEvaluator ruleConstraint = new 
RuleConstraintEvaluator();
+                        ruleConstraint.setDialect(constraint.getDialect());
+                        ruleConstraint.setName(constraint.getName());
+                        ruleConstraint.setPriority(constraint.getPriority());
+                        ruleConstraint.setDefault(constraint.isDefault());
+                        ((NodeImpl) node).setConstraint(outgoingConnection, 
ruleConstraint);
+                    } else if ("code".equals(constraint.getType())) {
+                        ReturnValueConstraintEvaluator returnValueConstraint = 
new ReturnValueConstraintEvaluator();
+                        
returnValueConstraint.setDialect(constraint.getDialect());
+                        returnValueConstraint.setName(constraint.getName());
+                        
returnValueConstraint.setPriority(constraint.getPriority());
+                        
returnValueConstraint.setDefault(constraint.isDefault());
+                        ((NodeImpl) node).setConstraint(outgoingConnection, 
returnValueConstraint);
 
-                ReturnValueDescr returnValueDescr = new ReturnValueDescr();
-                returnValueDescr.setText(constraint.getConstraint());
-                returnValueDescr.setResource(processDescr.getResource());
+                        ReturnValueDescr returnValueDescr = new 
ReturnValueDescr();
+                        returnValueDescr.setText(constraint.getConstraint());
+                        
returnValueDescr.setResource(processDescr.getResource());
 
-                ProcessDialect dialect = 
ProcessDialectRegistry.getDialect(constraint.getDialect());
-                dialect.getReturnValueEvaluatorBuilder().build(context, 
returnValueConstraint, returnValueDescr, (NodeImpl) node);
+                        ProcessDialect dialect = 
ProcessDialectRegistry.getDialect(constraint.getDialect());
+                        
dialect.getReturnValueEvaluatorBuilder().build(context, returnValueConstraint, 
returnValueDescr, (NodeImpl) node);
+                    }
+                }
             }
         }
 
diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/SplitNodeBuilder.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/SplitNodeBuilder.java
index 3d2194cd25..3fc60663da 100755
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/SplitNodeBuilder.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/process/builder/SplitNodeBuilder.java
@@ -18,8 +18,9 @@
  */
 package org.jbpm.process.builder;
 
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.Map;
 
 import org.drools.drl.ast.descr.ProcessDescr;
@@ -30,7 +31,6 @@ import 
org.jbpm.process.instance.impl.ReturnValueConstraintEvaluator;
 import org.jbpm.process.instance.impl.RuleConstraintEvaluator;
 import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.impl.ConnectionRef;
-import org.jbpm.workflow.core.impl.ConstraintImpl;
 import org.jbpm.workflow.core.impl.NodeImpl;
 import org.jbpm.workflow.core.node.Split;
 import org.kie.api.definition.process.Connection;
@@ -50,11 +50,11 @@ public class SplitNodeBuilder implements ProcessNodeBuilder 
{
             return;
         }
         // we need to clone the map, so we can update the original while 
iterating.
-        Map<ConnectionRef, Constraint> map = new 
HashMap<>(splitNode.getConstraints());
-        for (Iterator<Map.Entry<ConnectionRef, Constraint>> it = 
map.entrySet().iterator(); it.hasNext();) {
-            Map.Entry<ConnectionRef, Constraint> entry = it.next();
+        Map<ConnectionRef, Collection<Constraint>> map = 
splitNode.getConstraints();
+        for (Iterator<Map.Entry<ConnectionRef, Collection<Constraint>>> it = 
map.entrySet().iterator(); it.hasNext();) {
+            Map.Entry<ConnectionRef, Collection<Constraint>> entry = it.next();
             ConnectionRef connection = entry.getKey();
-            ConstraintImpl constraint = (ConstraintImpl) entry.getValue();
+            Collection<Constraint> constraints = entry.getValue();
             Connection outgoingConnection = null;
             for (Connection out : splitNode.getDefaultOutgoingConnections()) {
                 if (out.getToType().equals(connection.getToType())
@@ -65,35 +65,39 @@ public class SplitNodeBuilder implements ProcessNodeBuilder 
{
             if (outgoingConnection == null) {
                 throw new IllegalArgumentException("Could not find outgoing 
connection");
             }
-            if (constraint == null && splitNode.isDefault(outgoingConnection)) 
{
-                // do nothing since conditions are ignored for default 
sequence flow
-            } else if (constraint != null && 
"rule".equals(constraint.getType())) {
-                RuleConstraintEvaluator ruleConstraint = new 
RuleConstraintEvaluator();
-                ruleConstraint.setDialect(constraint.getDialect());
-                ruleConstraint.setName(constraint.getName());
-                ruleConstraint.setPriority(constraint.getPriority());
-                ruleConstraint.setDefault(constraint.isDefault());
-                ruleConstraint.setType(constraint.getType());
-                ruleConstraint.setConstraint(constraint.getConstraint());
-                splitNode.setConstraint(outgoingConnection, ruleConstraint);
-            } else if (constraint != null && 
"code".equals(constraint.getType())) {
-                ReturnValueConstraintEvaluator returnValueConstraint = new 
ReturnValueConstraintEvaluator();
-                returnValueConstraint.setDialect(constraint.getDialect());
-                returnValueConstraint.setName(constraint.getName());
-                returnValueConstraint.setPriority(constraint.getPriority());
-                returnValueConstraint.setDefault(constraint.isDefault());
-                returnValueConstraint.setType(constraint.getType());
-                
returnValueConstraint.setConstraint(constraint.getConstraint());
-                splitNode.setConstraint(outgoingConnection, 
returnValueConstraint);
+            if (!splitNode.isDefault(outgoingConnection) && constraints != 
null)
+                for (Constraint constraint : new LinkedList<>(constraints)) {
+                    if (constraint != null) {
+                        if ("rule".equals(constraint.getType())) {
+                            RuleConstraintEvaluator ruleConstraint = new 
RuleConstraintEvaluator();
+                            ruleConstraint.setDialect(constraint.getDialect());
+                            ruleConstraint.setName(constraint.getName());
+                            
ruleConstraint.setPriority(constraint.getPriority());
+                            ruleConstraint.setDefault(constraint.isDefault());
+                            ruleConstraint.setType(constraint.getType());
+                            
ruleConstraint.setConstraint(constraint.getConstraint());
+                            splitNode.setConstraint(outgoingConnection, 
ruleConstraint);
+                        } else if ("code".equals(constraint.getType())) {
+                            ReturnValueConstraintEvaluator 
returnValueConstraint = new ReturnValueConstraintEvaluator();
+                            
returnValueConstraint.setDialect(constraint.getDialect());
+                            
returnValueConstraint.setName(constraint.getName());
+                            
returnValueConstraint.setPriority(constraint.getPriority());
+                            
returnValueConstraint.setDefault(constraint.isDefault());
+                            
returnValueConstraint.setType(constraint.getType());
+                            
returnValueConstraint.setConstraint(constraint.getConstraint());
+                            splitNode.setConstraint(outgoingConnection, 
returnValueConstraint);
 
-                ReturnValueDescr returnValueDescr = new ReturnValueDescr();
-                returnValueDescr.setText(constraint.getConstraint());
-                returnValueDescr.setResource(processDescr.getResource());
+                            ReturnValueDescr returnValueDescr = new 
ReturnValueDescr();
+                            
returnValueDescr.setText(constraint.getConstraint());
+                            
returnValueDescr.setResource(processDescr.getResource());
 
-                ProcessDialect dialect = 
ProcessDialectRegistry.getDialect(constraint.getDialect());
-                dialect.getReturnValueEvaluatorBuilder().build(context, 
returnValueConstraint, returnValueDescr, (NodeImpl) node);
-            }
+                            ProcessDialect dialect = 
ProcessDialectRegistry.getDialect(constraint.getDialect());
+                            
dialect.getReturnValueEvaluatorBuilder().build(context, returnValueConstraint, 
returnValueDescr, (NodeImpl) node);
+                        }
+                    }
+                }
         }
+
     }
 
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
index 831354914d..72106aa8cf 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
@@ -20,6 +20,7 @@ package org.jbpm.ruleflow.core.validation;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -43,6 +44,7 @@ import org.jbpm.process.core.validation.ProcessValidator;
 import org.jbpm.process.core.validation.impl.ProcessValidationErrorImpl;
 import org.jbpm.ruleflow.core.Metadata;
 import org.jbpm.ruleflow.core.RuleFlowProcess;
+import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.DroolsAction;
 import org.jbpm.workflow.core.Node;
 import org.jbpm.workflow.core.WorkflowProcess;
@@ -268,10 +270,8 @@ public class RuleFlowProcessValidator implements 
ProcessValidator {
                 if (split.getType() == Split.TYPE_XOR || split.getType() == 
Split.TYPE_OR) {
                     for (final Iterator<Connection> it = 
split.getDefaultOutgoingConnections().iterator(); it.hasNext();) {
                         final Connection connection = it.next();
-                        if (split.getConstraint(connection) == null && 
!split.isDefault(connection)
-                                || (!split.isDefault(connection)
-                                        && 
(split.getConstraint(connection).getConstraint() == null
-                                                || 
split.getConstraint(connection).getConstraint().trim().length() == 0))) {
+                        Collection<Constraint> constraints = 
split.getConstraints(connection);
+                        if ((constraints == null || 
constraints.stream().allMatch(c -> c == null || c.getConstraint() == null || 
c.getConstraint().isBlank())) && !split.isDefault(connection)) {
                             addErrorMessage(process,
                                     node,
                                     errors,
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java
index e58e08bb24..694e3049c2 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java
@@ -19,15 +19,18 @@
 package org.jbpm.workflow.core.impl;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.jbpm.process.core.Context;
 import org.jbpm.process.core.ContextResolver;
 import org.jbpm.process.core.context.variable.Mappable;
+import org.jbpm.process.instance.impl.ReturnValueConstraintEvaluator;
 import org.jbpm.ruleflow.core.RuleFlowProcess;
 import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.Node;
@@ -53,7 +56,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
     private Map<String, Context> contexts = new HashMap<>();
     private Map<String, Object> metaData = new HashMap<>();
 
-    protected Map<ConnectionRef, Constraint> constraints = new HashMap<>();
+    protected Map<ConnectionRef, Collection<Constraint>> constraints = new 
HashMap<>();
 
     private IOSpecification ioSpecification;
     private MultiInstanceSpecification multiInstanceSpecification;
@@ -398,18 +401,23 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         this.metaData = metaData;
     }
 
-    public Constraint getConstraint(final Connection connection) {
+    public Collection<Constraint> getConstraints(final Connection connection) {
         if (connection == null) {
             throw new IllegalArgumentException("connection is null");
         }
 
         ConnectionRef ref = new ConnectionRef((String) 
connection.getMetaData().get("UniqueId"), connection.getTo().getId(), 
connection.getToType());
         return this.constraints.get(ref);
+    }
 
+    public Constraint getConstraint(final Connection connection) {
+        Collection<Constraint> constraints = getConstraints(connection);
+        return constraints != null ? constraints.iterator().next() : null;
     }
 
     public Constraint internalGetConstraint(final ConnectionRef ref) {
-        return this.constraints.get(ref);
+        Collection<Constraint> constraints = this.constraints.get(ref);
+        return constraints != null ? constraints.iterator().next() : null;
     }
 
     public void setConstraint(final Connection connection,
@@ -431,10 +439,12 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
             throw new IllegalArgumentException(
                     "A " + this.getName() + " node only accepts constraints 
linked to a connection");
         }
-        this.constraints.put(connectionRef, constraint);
+        Collection<Constraint> values = 
this.constraints.computeIfAbsent(connectionRef, r -> new ArrayList<>());
+        values.removeIf(v -> 
!ReturnValueConstraintEvaluator.class.isInstance(v) && 
Objects.equals(v.getConstraint(), constraint.getConstraint()));
+        values.add(constraint);
     }
 
-    public Map<ConnectionRef, Constraint> getConstraints() {
+    public Map<ConnectionRef, Collection<Constraint>> getConstraints() {
         return Collections.unmodifiableMap(this.constraints);
     }
 
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/Split.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/Split.java
index 9186851e9c..659b877dc6 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/Split.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/Split.java
@@ -18,8 +18,7 @@
  */
 package org.jbpm.workflow.core.node;
 
-import java.util.Collections;
-import java.util.Map;
+import java.util.Collection;
 
 import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.Node;
@@ -86,23 +85,24 @@ public class Split extends NodeImpl implements 
Constrainable {
 
         if (this.type == TYPE_OR || this.type == TYPE_XOR) {
             ConnectionRef ref = new ConnectionRef((String) 
connection.getMetaData().get("UniqueId"), connection.getTo().getId(), 
connection.getToType());
-            Constraint constraint = this.constraints.get(ref);
+            Collection<Constraint> constraints = this.constraints.get(ref);
+            if (constraints != null) {
+                for (Constraint constraint : constraints) {
+                    if (constraint != null) {
+                        return constraint.isDefault();
+                    }
+                }
+            }
             String defaultConnection = (String) getMetaData().get("Default");
             String connectionId = (String) 
connection.getMetaData().get("UniqueId");
-            if (constraint != null) {
-                return constraint.isDefault();
-            } else if (constraint == null && 
connectionId.equals(defaultConnection)) {
-                return true;
-            } else {
-                return false;
-            }
+            return connectionId.equals(defaultConnection);
         }
         throw new UnsupportedOperationException("Constraints are " +
                 "only supported with XOR or OR split types, not with: " + 
getType());
     }
 
     @Override
-    public Constraint getConstraint(final Connection connection) {
+    public Collection<Constraint> getConstraints(final Connection connection) {
         if (connection == null) {
             throw new IllegalArgumentException("connection is null");
         }
@@ -115,11 +115,6 @@ public class Split extends NodeImpl implements 
Constrainable {
                 "only supported with XOR or OR split types, not with: " + 
getType());
     }
 
-    @Override
-    public Constraint internalGetConstraint(final ConnectionRef ref) {
-        return this.constraints.get(ref);
-    }
-
     @Override
     public void setConstraint(final Connection connection,
             final Constraint constraint) {
@@ -139,20 +134,6 @@ public class Split extends NodeImpl implements 
Constrainable {
         }
     }
 
-    @Override
-    public void addConstraint(ConnectionRef connectionRef, Constraint 
constraint) {
-        if (connectionRef == null) {
-            throw new IllegalArgumentException(
-                    "A split node only accepts constraints linked to a 
connection");
-        }
-        this.constraints.put(connectionRef, constraint);
-    }
-
-    @Override
-    public Map<ConnectionRef, Constraint> getConstraints() {
-        return Collections.unmodifiableMap(this.constraints);
-    }
-
     @Override
     public void validateAddIncomingConnection(final String type, final 
Connection connection) {
         super.validateAddIncomingConnection(type, connection);
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/StateNode.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/StateNode.java
index b760ec9882..013876f710 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/StateNode.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/StateNode.java
@@ -18,60 +18,7 @@
  */
 package org.jbpm.workflow.core.node;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import org.jbpm.workflow.core.Constraint;
-import org.jbpm.workflow.core.impl.ConnectionRef;
-import org.kie.api.definition.process.Connection;
-
 public class StateNode extends CompositeContextNode implements Constrainable {
 
     private static final long serialVersionUID = 510l;
-
-    private Map<ConnectionRef, Constraint> constraints = new HashMap<>();
-
-    public void setConstraints(Map<ConnectionRef, Constraint> constraints) {
-        this.constraints = constraints;
-    }
-
-    @Override
-    public void setConstraint(final Connection connection, final Constraint 
constraint) {
-        if (connection == null) {
-            throw new IllegalArgumentException("connection is null");
-        }
-        if (!getDefaultOutgoingConnections().contains(connection)) {
-            throw new IllegalArgumentException("connection is unknown:" + 
connection);
-        }
-        addConstraint(new ConnectionRef((String) 
connection.getMetaData().get("UniqueId"),
-                connection.getTo().getId(), connection.getToType()), 
constraint);
-    }
-
-    @Override
-    public void addConstraint(ConnectionRef connectionRef, Constraint 
constraint) {
-        if (connectionRef == null) {
-            throw new IllegalArgumentException(
-                    "A state node only accepts constraints linked to a 
connection");
-        }
-        constraints.put(connectionRef, constraint);
-    }
-
-    public Constraint getConstraint(ConnectionRef connectionRef) {
-        return constraints.get(connectionRef);
-    }
-
-    @Override
-    public Map<ConnectionRef, Constraint> getConstraints() {
-        return constraints;
-    }
-
-    @Override
-    public Constraint getConstraint(final Connection connection) {
-        if (connection == null) {
-            throw new IllegalArgumentException("connection is null");
-        }
-        ConnectionRef ref = new ConnectionRef((String) 
connection.getMetaData().get("UniqueId"), connection.getTo().getId(), 
connection.getToType());
-        return this.constraints.get(ref);
-    }
-
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
index eecc9e3378..0cfd7ea001 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/NodeInstanceImpl.java
@@ -46,6 +46,7 @@ import org.jbpm.process.instance.impl.Action;
 import org.jbpm.process.instance.impl.ConstraintEvaluator;
 import org.jbpm.util.ContextFactory;
 import org.jbpm.util.PatternConstants;
+import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.Node;
 import org.jbpm.workflow.core.impl.NodeImpl;
 import org.jbpm.workflow.instance.WorkflowProcessInstance;
@@ -311,7 +312,7 @@ public abstract class NodeInstanceImpl implements 
org.jbpm.workflow.instance.Nod
 
         List<Connection> connections = null;
         if (node != null) {
-            if ("true".equals(System.getProperty("jbpm.enable.multi.con")) && 
((NodeImpl) node).getConstraints().size() > 0) {
+            if ("true".equals(System.getProperty("jbpm.enable.multi.con")) && 
!((NodeImpl) node).getConstraints().isEmpty()) {
                 int priority;
                 connections = ((NodeImpl) 
node).getDefaultOutgoingConnections();
                 boolean found = false;
@@ -323,13 +324,17 @@ public abstract class NodeInstanceImpl implements 
org.jbpm.workflow.instance.Nod
                     Connection selectedConnection = null;
                     ConstraintEvaluator selectedConstraint = null;
                     for (final Connection connection : outgoingCopy) {
-                        ConstraintEvaluator constraint = (ConstraintEvaluator) 
((NodeImpl) node).getConstraint(connection);
-                        if (constraint != null
-                                && constraint.getPriority() < priority
-                                && !constraint.isDefault()) {
-                            priority = constraint.getPriority();
-                            selectedConnection = connection;
-                            selectedConstraint = constraint;
+                        Collection<Constraint> constraints = ((NodeImpl) 
node).getConstraints(connection);
+                        if (constraints != null) {
+                            for (Constraint constraint : constraints) {
+                                if (constraint instanceof ConstraintEvaluator 
&& constraint.getPriority() < priority
+                                        && !constraint.isDefault()) {
+                                    priority = constraint.getPriority();
+                                    selectedConnection = connection;
+                                    selectedConstraint = (ConstraintEvaluator) 
constraint;
+                                }
+
+                            }
                         }
                     }
                     if (selectedConstraint == null) {
@@ -352,10 +357,17 @@ public abstract class NodeInstanceImpl implements 
org.jbpm.workflow.instance.Nod
                 }
                 if (!found) {
                     for (final Connection connection : connections) {
-                        ConstraintEvaluator constraint = (ConstraintEvaluator) 
((NodeImpl) node).getConstraint(connection);
-                        if (constraint.isDefault()) {
-                            triggerConnection(connection);
-                            found = true;
+                        Collection<Constraint> constraints = ((NodeImpl) 
node).getConstraints(connection);
+                        if (constraints != null) {
+                            for (Constraint constraint : constraints) {
+                                if (constraint.isDefault()) {
+                                    triggerConnection(connection);
+                                    found = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if (found) {
                             break;
                         }
                     }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/SplitInstance.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/SplitInstance.java
index a211da11fb..47fb7610c8 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/SplitInstance.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/SplitInstance.java
@@ -19,6 +19,7 @@
 package org.jbpm.workflow.instance.node;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -33,6 +34,7 @@ import org.jbpm.process.instance.ContextInstanceContainer;
 import org.jbpm.process.instance.InternalProcessRuntime;
 import org.jbpm.process.instance.context.exclusive.ExclusiveGroupInstance;
 import org.jbpm.process.instance.impl.ConstraintEvaluator;
+import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.Node;
 import org.jbpm.workflow.core.node.Split;
 import org.jbpm.workflow.instance.NodeInstanceContainer;
@@ -83,21 +85,25 @@ public class SplitInstance extends NodeInstanceImpl {
                 Connection selected = null;
                 for (final Iterator<Connection> iterator = 
outgoing.iterator(); iterator.hasNext();) {
                     final Connection connection = iterator.next();
-                    ConstraintEvaluator constraint = (ConstraintEvaluator) 
split.getConstraint(connection);
-                    if (constraint != null && constraint.getPriority() < 
priority && !constraint.isDefault()) {
-                        try {
-                            if (constraint.evaluate(this,
-                                    connection,
-                                    constraint)) {
-                                selected = connection;
-                                priority = constraint.getPriority();
+                    Collection<Constraint> constraints = 
split.getConstraints(connection);
+                    if (constraints != null) {
+                        for (Constraint constraint : constraints) {
+                            if (constraint instanceof ConstraintEvaluator && 
constraint.getPriority() < priority && !constraint.isDefault()) {
+                                try {
+                                    if (((ConstraintEvaluator) 
constraint).evaluate(this,
+                                            connection,
+                                            constraint)) {
+                                        selected = connection;
+                                        priority = constraint.getPriority();
+                                    }
+                                } catch (RuntimeException e) {
+                                    throw new RuntimeException(
+                                            "Exception when trying to evaluate 
constraint "
+                                                    + constraint.getName() + " 
in split "
+                                                    + split.getName(),
+                                            e);
+                                }
                             }
-                        } catch (RuntimeException e) {
-                            throw new RuntimeException(
-                                    "Exception when trying to evaluate 
constraint "
-                                            + constraint.getName() + " in 
split "
-                                            + split.getName(),
-                                    e);
                         }
                     }
                 }
@@ -133,14 +139,16 @@ public class SplitInstance extends NodeInstanceImpl {
                     ConstraintEvaluator selectedConstraint = null;
                     for (final Iterator<Connection> iterator = 
outgoingCopy.iterator(); iterator.hasNext();) {
                         final Connection connection = iterator.next();
-                        ConstraintEvaluator constraint = (ConstraintEvaluator) 
split.getConstraint(connection);
-
-                        if (constraint != null
-                                && constraint.getPriority() < priority
-                                && !constraint.isDefault()) {
-                            priority = constraint.getPriority();
-                            selectedConnection = connection;
-                            selectedConstraint = constraint;
+                        Collection<Constraint> constraints = 
split.getConstraints(connection);
+                        if (constraints != null) {
+                            for (Constraint constraint : constraints) {
+                                if (constraint instanceof ConstraintEvaluator 
&& constraint.getPriority() < priority
+                                        && !constraint.isDefault()) {
+                                    priority = constraint.getPriority();
+                                    selectedConnection = connection;
+                                    selectedConstraint = (ConstraintEvaluator) 
constraint;
+                                }
+                            }
                         }
                     }
                     if (selectedConstraint == null) {
@@ -163,13 +171,22 @@ public class SplitInstance extends NodeInstanceImpl {
                     triggerNodeInstance(nodeInstance.getNodeInstance(), 
nodeInstance.getToType());
                 }
                 if (!found) {
-                    for (final Iterator<Connection> iterator = 
outgoing.iterator(); iterator.hasNext();) {
+                    final Iterator<Connection> iterator = outgoing.iterator();
+                    while (!found && iterator.hasNext()) {
                         final Connection connection = iterator.next();
-                        ConstraintEvaluator constraint = (ConstraintEvaluator) 
split.getConstraint(connection);
-                        if (constraint != null && constraint.isDefault() || 
split.isDefault(connection)) {
+                        Collection<Constraint> constraints = 
split.getConstraints(connection);
+                        if (constraints != null) {
+                            for (Constraint constraint : constraints) {
+                                if (constraint != null && 
constraint.isDefault()) {
+                                    triggerConnection(connection);
+                                    found = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if (!found && split.isDefault(connection)) {
                             triggerConnection(connection);
                             found = true;
-                            break;
                         }
                     }
                 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateNodeInstance.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateNodeInstance.java
index 34e983bd1c..37a928a293 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateNodeInstance.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateNodeInstance.java
@@ -18,6 +18,8 @@
  */
 package org.jbpm.workflow.instance.node;
 
+import java.util.Collection;
+
 import org.drools.core.common.InternalAgenda;
 import org.drools.core.rule.consequence.InternalMatch;
 import org.jbpm.workflow.core.Constraint;
@@ -50,17 +52,22 @@ public class StateNodeInstance extends 
CompositeContextNodeInstance implements E
         Connection selected = null;
         int priority = Integer.MAX_VALUE;
         for (Connection connection : 
stateNode.getOutgoingConnections(Node.CONNECTION_DEFAULT_TYPE)) {
-            Constraint constraint = stateNode.getConstraint(connection);
-            if (constraint != null && constraint.getPriority() < priority) {
-                String rule = "RuleFlowStateNode-" + 
getProcessInstance().getProcessId() + "-" +
-                        getStateNode().getUniqueId() + "-" +
-                        connection.getTo().getId() + "-" +
-                        connection.getToType();
-                boolean isActive = ((InternalAgenda) 
getProcessInstance().getKnowledgeRuntime().getAgenda())
-                        .isRuleActiveInRuleFlowGroup("DROOLS_SYSTEM", rule, 
getProcessInstance().getStringId());
-                if (isActive) {
-                    selected = connection;
-                    priority = constraint.getPriority();
+            Collection<Constraint> constraints = 
stateNode.getConstraints(connection);
+
+            if (constraints != null) {
+                for (Constraint constraint : constraints) {
+                    if (constraint.getPriority() < priority) {
+                        String rule = "RuleFlowStateNode-" + 
getProcessInstance().getProcessId() + "-" +
+                                getStateNode().getUniqueId() + "-" +
+                                connection.getTo().getId() + "-" +
+                                connection.getToType();
+                        boolean isActive = ((InternalAgenda) 
getProcessInstance().getKnowledgeRuntime().getAgenda())
+                                .isRuleActiveInRuleFlowGroup("DROOLS_SYSTEM", 
rule, getProcessInstance().getStringId());
+                        if (isActive) {
+                            selected = connection;
+                            priority = constraint.getPriority();
+                        }
+                    }
                 }
             }
         }
@@ -71,6 +78,7 @@ public class StateNodeInstance extends 
CompositeContextNodeInstance implements E
             addTriggerListener();
             addActivationListener();
         }
+
     }
 
     @Override
@@ -84,14 +92,19 @@ public class StateNodeInstance extends 
CompositeContextNodeInstance implements E
             if (event instanceof String) {
                 for (Connection connection : 
getStateNode().getOutgoingConnections(Node.CONNECTION_DEFAULT_TYPE)) {
                     boolean selected = false;
-                    Constraint constraint = 
getStateNode().getConstraint(connection);
-                    if (constraint == null) {
+                    Collection<Constraint> constraints = 
getStateNode().getConstraints(connection);
+                    if (constraints == null) {
                         if (event.equals(connection.getTo().getName())) {
                             selected = true;
                         }
-                    } else if (event.equals(constraint.getName())) {
-                        selected = true;
-                    }
+                    } else
+                        for (Constraint constraint : constraints) {
+
+                            if (event.equals(constraint.getName())) {
+                                selected = true;
+                                break;
+                            }
+                        }
                     if (selected) {
                         triggerEvent(ExtendedNodeImpl.EVENT_NODE_EXIT);
                         removeEventListeners();
@@ -146,14 +159,15 @@ public class StateNodeInstance extends 
CompositeContextNodeInstance implements E
     public void activationCreated(MatchCreatedEvent event) {
         Connection selected = null;
         for (Connection connection : 
getNode().getOutgoingConnections(Node.CONNECTION_DEFAULT_TYPE)) {
-            Constraint constraint = getStateNode().getConstraint(connection);
-            if (constraint != null) {
+            Collection<Constraint> constraints = 
getStateNode().getConstraints(connection);
+            if (constraints != null) {
                 String constraintName = getActivationEventType() + "-"
                         + connection.getTo().getId() + "-" + 
connection.getToType();
                 if (constraintName.equals(event.getMatch().getRule().getName())
                         && checkProcessInstance((InternalMatch) 
event.getMatch())) {
                     selected = connection;
                 }
+
             }
         }
         if (selected != null) {
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/process/ProcessGenerationIT.java
 
b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/process/ProcessGenerationIT.java
index dd896a2147..c728af6aba 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/process/ProcessGenerationIT.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/process/ProcessGenerationIT.java
@@ -532,11 +532,11 @@ public class ProcessGenerationIT extends 
AbstractCodegenIT {
             assertThat(cNode.getConstraints()).isNull();
             return;
         }
-        Map<ConnectionRef, Constraint> expected = eNode.getConstraints();
-        Map<ConnectionRef, Constraint> current = cNode.getConstraints();
+        Map<ConnectionRef, Collection<Constraint>> expected = 
eNode.getConstraints();
+        Map<ConnectionRef, Collection<Constraint>> current = 
cNode.getConstraints();
         assertThat(current).hasSameSizeAs(expected);
         expected.forEach((conn, constraint) -> {
-            Optional<Map.Entry<ConnectionRef, Constraint>> currentEntry = 
current.entrySet()
+            Optional<Map.Entry<ConnectionRef, Collection<Constraint>>> 
currentEntry = current.entrySet()
                     .stream()
                     .filter(e -> e.getKey().getConnectionId() == null && 
conn.getConnectionId() == null ||
                             
e.getKey().getConnectionId().equals(conn.getConnectionId()))
@@ -545,13 +545,15 @@ public class ProcessGenerationIT extends 
AbstractCodegenIT {
             ConnectionRef currentConn = currentEntry.get().getKey();
             assertThat(currentConn.getNodeId()).isEqualTo(conn.getNodeId());
             assertThat(currentConn.getToType()).isEqualTo(conn.getToType());
-            Constraint currentConstraint = currentEntry.get().getValue();
+            Collection<Constraint> constraints = currentEntry.get().getValue();
             if (constraint == null) {
-                assertThat(currentConstraint).isNull();
+                assertThat(constraints).isNull();
             } else {
+                Constraint currentConstraint = constraints.iterator().next();
+                Constraint expectedConstraint = constraint.iterator().next();
                 assertThat(currentConstraint).isNotNull();
-                
assertThat(currentConstraint.getPriority()).isEqualTo(constraint.getPriority());
-                
assertThat(currentConstraint.getDialect()).isEqualTo(constraint.getDialect());
+                
assertThat(currentConstraint.getPriority()).isEqualTo(expectedConstraint.getPriority());
+                
assertThat(currentConstraint.getDialect()).isEqualTo(expectedConstraint.getDialect());
                 
assertThat(currentConstraint.getName()).isEqualTo(conn.getConnectionId());
                 
assertThat(currentConstraint.getType()).isEqualTo(CONNECTION_DEFAULT_TYPE);
             }
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java
index 4341ea27ef..f0f5a11337 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/ServerlessWorkflowParsingTest.java
@@ -18,6 +18,7 @@
  */
 package org.kie.kogito.serverless.workflow;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.jbpm.ruleflow.core.Metadata;
@@ -577,12 +578,7 @@ public class ServerlessWorkflowParsingTest extends 
AbstractServerlessWorkflowPar
         assertThat(split.getType()).isEqualTo(2);
         assertThat(split.getConstraints()).hasSize(2);
 
-        boolean haveDefaultConstraint = false;
-        for (Constraint constraint : split.getConstraints().values()) {
-            haveDefaultConstraint = haveDefaultConstraint || 
constraint.isDefault();
-        }
-
-        assertThat(haveDefaultConstraint).isTrue();
+        assertHaveDefaultConstraint(split);
     }
 
     @ParameterizedTest
@@ -603,12 +599,11 @@ public class ServerlessWorkflowParsingTest extends 
AbstractServerlessWorkflowPar
         assertThat(split.getType()).isEqualTo(2);
         assertThat(split.getConstraints()).hasSize(2);
 
-        boolean haveDefaultConstraint = false;
-        for (Constraint constraint : split.getConstraints().values()) {
-            haveDefaultConstraint = haveDefaultConstraint || 
constraint.isDefault();
-        }
+        assertHaveDefaultConstraint(split);
+    }
 
-        assertThat(haveDefaultConstraint).isTrue();
+    private void assertHaveDefaultConstraint(Split split) {
+        
assertThat(split.getConstraints().values().stream().flatMap(Collection::stream).anyMatch(Constraint::isDefault)).isTrue();
     }
 
     @ParameterizedTest
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/WorkflowTestUtils.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/WorkflowTestUtils.java
index 5969a3e46b..ba280e7dc6 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/WorkflowTestUtils.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/WorkflowTestUtils.java
@@ -26,6 +26,7 @@ import java.io.Reader;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 
@@ -150,6 +151,7 @@ public class WorkflowTestUtils {
 
     public static void assertConstraintIsDefault(Split splitNode, String 
constraintName) {
         Constraint constraint = splitNode.getConstraints().values().stream()
+                .flatMap(Collection::stream)
                 .filter(c -> constraintName.equals(c.getName()))
                 .findFirst().orElse(null);
         assertThat(constraint)
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java
index 728fe91a56..5da4db34d0 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/java/org/kie/kogito/serverless/workflow/executor/StaticWorkflowApplicationTest.java
@@ -25,6 +25,7 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Map;
 import java.util.stream.Stream;
 
 import org.junit.jupiter.api.Test;
@@ -32,6 +33,7 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.kie.kogito.serverless.workflow.actions.SysoutAction;
+import org.kie.kogito.serverless.workflow.utils.ServerlessWorkflowUtils;
 import org.kie.kogito.serverless.workflow.utils.WorkflowFormat;
 import org.slf4j.LoggerFactory;
 
@@ -76,6 +78,19 @@ class StaticWorkflowApplicationTest {
         }
     }
 
+    @Test
+    void switchFile() throws IOException {
+        try (
+                StaticWorkflowApplication application = 
StaticWorkflowApplication.create();
+                Reader reader = new 
InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("switch.yaml")))
 {
+            Workflow workflow = ServerlessWorkflowUtils.getWorkflow(reader, 
WorkflowFormat.YAML);
+            assertThat(application.execute(workflow, Map.of("condition1", 
false, "condition2", false)).getWorkflowdata().get("output")).isEqualTo(new 
TextNode("default"));
+            assertThat(application.execute(workflow, Map.of("condition1", 
true, "condition2", false)).getWorkflowdata().get("output")).isEqualTo(new 
TextNode("branch1"));
+            assertThat(application.execute(workflow, Map.of("condition1", 
false, "condition2", true)).getWorkflowdata().get("output")).isEqualTo(new 
TextNode("branch1"));
+            assertThat(application.execute(workflow, Map.of("condition1", 
true, "condition2", true)).getWorkflowdata().get("output")).isEqualTo(new 
TextNode("branch1"));
+        }
+    }
+
     @ParameterizedTest
     @MethodSource("interpolationParameters")
     void interpolationFile(String fileName, WorkflowFormat format) throws 
IOException {
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/switch.yaml
 
b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/switch.yaml
new file mode 100644
index 0000000000..1af16001cb
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-executor-core/src/test/resources/switch.yaml
@@ -0,0 +1,32 @@
+version: 1.0.0
+specVersion: "0.8"
+id: Switch test
+name: Switch test
+start: switch test
+states:
+  - name: switch test
+    type: switch
+    dataConditions:
+      - name: Branch1-Name
+        condition: .condition1
+        transition: branch1
+      - name: Branch2-Name
+        condition: .condition2
+        transition: branch1
+
+    defaultCondition:
+      transition: default
+
+  - name: branch1
+    type: inject
+    data:
+      output: branch1
+    end: true
+
+
+  - name: default
+    type: inject
+    data:
+      output: default
+    end: true
+ 
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to