From 8ad5e1eb8bd2e6e90cc2305ef81200e474ea7bb9 Mon Sep 17 00:00:00 2001 From: Yvo Swillens Date: Mon, 9 Oct 2023 08:16:11 +0200 Subject: [PATCH] fixed DecisionService execution stack not updating when having ComposeDecisionResultBehavior hitpolicy (#3760) --- .../impl/hitpolicy/AbstractHitPolicy.java | 8 +++ .../ComposeDecisionResultBehavior.java | 4 ++ .../engine/impl/hitpolicy/HitPolicyAny.java | 9 ++- .../impl/hitpolicy/HitPolicyCollect.java | 4 ++ .../impl/hitpolicy/HitPolicyOutputOrder.java | 24 +++---- .../impl/hitpolicy/HitPolicyUnique.java | 9 +-- .../test/runtime/drd/DecisionServiceTest.java | 18 +++++ .../runtime/decisionServiceStackUpdate.dmn | 72 +++++++++++++++++++ 8 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 modules/flowable-dmn-engine/src/test/resources/org/flowable/dmn/engine/test/runtime/decisionServiceStackUpdate.dmn diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/AbstractHitPolicy.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/AbstractHitPolicy.java index ce0587b7a57..6a5e355cdd2 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/AbstractHitPolicy.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/AbstractHitPolicy.java @@ -63,8 +63,16 @@ public void composeRuleResult(int ruleNumber, String outputName, Object outputVa @Override public void composeDecisionResults(ELExecutionContext executionContext) { List> decisionResults = new ArrayList<>(executionContext.getRuleResults().values()); + + updateStackWithDecisionResults(decisionResults, executionContext); + DecisionExecutionAuditContainer auditContainer = executionContext.getAuditContainer(); auditContainer.setDecisionResult(decisionResults); auditContainer.setMultipleResults(multipleResults); } + + @Override + public void updateStackWithDecisionResults(List> decisionResults, ELExecutionContext executionContext) { + decisionResults.forEach(result -> result.forEach((k, v) -> executionContext.getStackVariables().put(k, v))); + } } diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/ComposeDecisionResultBehavior.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/ComposeDecisionResultBehavior.java index ab2c0a6bd64..11b82950533 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/ComposeDecisionResultBehavior.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/ComposeDecisionResultBehavior.java @@ -12,6 +12,9 @@ */ package org.flowable.dmn.engine.impl.hitpolicy; +import java.util.List; +import java.util.Map; + import org.flowable.dmn.engine.impl.el.ELExecutionContext; /** @@ -20,4 +23,5 @@ public interface ComposeDecisionResultBehavior { void composeDecisionResults(ELExecutionContext executionContext); + void updateStackWithDecisionResults(List> decisionResults, ELExecutionContext executionContext); } diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyAny.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyAny.java index c84f7c94087..530f5006967 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyAny.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyAny.java @@ -73,7 +73,14 @@ public void composeDecisionResults(final ELExecutionContext executionContext) { if (CommandContextUtil.getDmnEngineConfiguration().isStrictMode() == false && validationFailed) { executionContext.getAuditContainer().setValidationMessage(String.format("HitPolicy %s violated; multiple valid rules with different outcomes. Setting last valid rule result as final result.", getHitPolicyName())); } - executionContext.getAuditContainer().addDecisionResultObject(ruleResults.get(ruleResults.size() - 1)); + + List> decisionResults = new ArrayList<>(); + decisionResults.add(ruleResults.get(ruleResults.size() - 1)); + + updateStackWithDecisionResults(decisionResults, executionContext); + + // put decision results on audit container + executionContext.getAuditContainer().setDecisionResult(decisionResults); } } diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyCollect.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyCollect.java index 4c20610a22f..52a797eb11f 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyCollect.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyCollect.java @@ -67,6 +67,10 @@ public void composeDecisionResults(ELExecutionContext executionContext) { } } } + + updateStackWithDecisionResults(decisionResults, executionContext); + + // put decision results on audit container executionContext.getAuditContainer().setDecisionResult(decisionResults); // the `multipleResults` flag depends on the aggregator. If there is no aggregation there are more results. executionContext.getAuditContainer().setMultipleResults(isMultipleResults(executionContext.getAggregator())); diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyOutputOrder.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyOutputOrder.java index 1e7f6ff7da6..0ff853890fe 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyOutputOrder.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyOutputOrder.java @@ -61,23 +61,21 @@ public void composeDecisionResults(final ELExecutionContext executionContext) { } // sort on predefined list(s) of output values - ruleResults.sort(new Comparator>() { - - @Override - public int compare(Map o1, Map o2) { - CompareToBuilder compareToBuilder = new CompareToBuilder(); - for (Map.Entry> entry : executionContext.getOutputValues().entrySet()) { - List outputValues = entry.getValue(); - if (outputValues != null && !outputValues.isEmpty()) { - compareToBuilder.append(o1.get(entry.getKey()), o2.get(entry.getKey()), - new OutputOrderComparator<>(outputValues.toArray(new Comparable[outputValues.size()]))); - compareToBuilder.toComparison(); - } + ruleResults.sort((o1, o2) -> { + CompareToBuilder compareToBuilder = new CompareToBuilder(); + for (Map.Entry> entry : executionContext.getOutputValues().entrySet()) { + List outputValues = entry.getValue(); + if (outputValues != null && !outputValues.isEmpty()) { + compareToBuilder.append(o1.get(entry.getKey()), o2.get(entry.getKey()), + new OutputOrderComparator<>(outputValues.toArray(new Comparable[outputValues.size()]))); + compareToBuilder.toComparison(); } - return compareToBuilder.toComparison(); } + return compareToBuilder.toComparison(); }); + updateStackWithDecisionResults(ruleResults, executionContext); + DecisionExecutionAuditContainer auditContainer = executionContext.getAuditContainer(); auditContainer.setDecisionResult(ruleResults); auditContainer.setMultipleResults(true); diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyUnique.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyUnique.java index 3226be59d96..b8f1c6386f7 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyUnique.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/hitpolicy/HitPolicyUnique.java @@ -57,7 +57,7 @@ public void evaluateRuleValidity(int ruleNumber, ELExecutionContext executionCon @Override public void composeDecisionResults(ELExecutionContext executionContext) { List> ruleResults = new ArrayList<>(executionContext.getRuleResults().values()); - List> decisionResult = null; + List> decisionResults; if (ruleResults.size() > 1 && CommandContextUtil.getDmnEngineConfiguration().isStrictMode() == false) { Map lastResult = new HashMap<>(); @@ -71,11 +71,12 @@ public void composeDecisionResults(ELExecutionContext executionContext) { } executionContext.getAuditContainer().setValidationMessage(String.format("HitPolicy %s violated; multiple valid rules. Setting last valid rule result as final result.", getHitPolicyName())); - decisionResult = Collections.singletonList(lastResult); + decisionResults = Collections.singletonList(lastResult); } else { - decisionResult = ruleResults; + decisionResults = ruleResults; } - executionContext.getAuditContainer().setDecisionResult(decisionResult); + updateStackWithDecisionResults(decisionResults, executionContext); + executionContext.getAuditContainer().setDecisionResult(decisionResults); } } diff --git a/modules/flowable-dmn-engine/src/test/java/org/flowable/dmn/engine/test/runtime/drd/DecisionServiceTest.java b/modules/flowable-dmn-engine/src/test/java/org/flowable/dmn/engine/test/runtime/drd/DecisionServiceTest.java index cfe3c74653b..5315f39bf6a 100644 --- a/modules/flowable-dmn-engine/src/test/java/org/flowable/dmn/engine/test/runtime/drd/DecisionServiceTest.java +++ b/modules/flowable-dmn-engine/src/test/java/org/flowable/dmn/engine/test/runtime/drd/DecisionServiceTest.java @@ -349,4 +349,22 @@ public void executeDecisionServiceWithSingleResultMultipleResults() { .isInstanceOf(FlowableException.class) .hasMessageContaining("more than one result in decision: decision1"); } + + @Test + @DmnDeployment(resources = "org/flowable/dmn/engine/test/runtime/decisionServiceStackUpdate.dmn") + public void executeDecisionServiceWithCollectStackUpdate() { + DmnEngine dmnEngine = flowableDmnRule.getDmnEngine(); + DmnDecisionService dmnRuleService = dmnEngine.getDmnDecisionService(); + + Map result = dmnRuleService.createExecuteDecisionBuilder() + .decisionKey("expandedDecisionService") + .variable("inputVariable1", 5D) + .executeDecisionServiceWithSingleResult(); + + Map expectedResult = new HashMap<>(); + expectedResult.put("serviceOutput1", "larger than 30"); + + assertThat(result) + .containsAllEntriesOf(expectedResult); + } } diff --git a/modules/flowable-dmn-engine/src/test/resources/org/flowable/dmn/engine/test/runtime/decisionServiceStackUpdate.dmn b/modules/flowable-dmn-engine/src/test/resources/org/flowable/dmn/engine/test/runtime/decisionServiceStackUpdate.dmn new file mode 100644 index 00000000000..86822bf8fd4 --- /dev/null +++ b/modules/flowable-dmn-engine/src/test/resources/org/flowable/dmn/engine/test/runtime/decisionServiceStackUpdate.dmn @@ -0,0 +1,72 @@ + + + + + + + + + outputVariable1 + + + + + + 40]]> + + + + + + + + + + + + + + inputVariable1 + + + + + + + + + 10 + + + + + + + + 20 + + + + + + + + 30 + + + + + + + + 30 + + + + + + + + + \ No newline at end of file