diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CaseTaskXmlConverter.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CaseTaskXmlConverter.java index 4a0bd67fd22..6edd7c848e5 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CaseTaskXmlConverter.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CaseTaskXmlConverter.java @@ -44,6 +44,11 @@ protected CmmnElement convert(XMLStreamReader xtr, ConversionHelper conversionHe caseTask.setInheritBusinessKey(Boolean.parseBoolean(inheritBusinessKey)); } + String inheritVariables = xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE, CmmnXmlConstants.ATTRIBUTE_INHERIT_VARIABLES); + if (inheritVariables != null) { + caseTask.setInheritVariables(Boolean.parseBoolean(inheritVariables)); + } + String fallbackToDefaultTenantValue = xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE, CmmnXmlConstants.ATTRIBUTE_FALLBACK_TO_DEFAULT_TENANT); if (fallbackToDefaultTenantValue != null) { caseTask.setFallbackToDefaultTenant(Boolean.valueOf(fallbackToDefaultTenantValue)); diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConstants.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConstants.java index fce5fd993ce..f1bf6797d1c 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConstants.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConstants.java @@ -142,6 +142,7 @@ public interface CmmnXmlConstants { String ATTRIBUTE_FALLBACK_TO_DEFAULT_TENANT = "fallbackToDefaultTenant"; String ATTRIBUTE_BUSINESS_KEY = "businessKey"; String ATTRIBUTE_INHERIT_BUSINESS_KEY = "inheritBusinessKey"; + String ATTRIBUTE_INHERIT_VARIABLES = "inheritVariables"; String ATTRIBUTE_ID_VARIABLE_NAME = "idVariableName"; String ELEMENT_CASE_REF_EXPRESSION = "caseRefExpression"; diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ProcessTaskXmlConverter.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ProcessTaskXmlConverter.java index c614f5fdc3f..67aab2d3f09 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ProcessTaskXmlConverter.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/ProcessTaskXmlConverter.java @@ -44,6 +44,11 @@ protected CmmnElement convert(XMLStreamReader xtr, ConversionHelper conversionHe processTask.setInheritBusinessKey(Boolean.parseBoolean(inheritBusinessKey)); } + String inheritVariables = xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE, CmmnXmlConstants.ATTRIBUTE_INHERIT_VARIABLES); + if (inheritVariables != null) { + processTask.setInheritVariables(Boolean.parseBoolean(inheritVariables)); + } + String fallbackToDefaultTenantValue = xtr.getAttributeValue(CmmnXmlConstants.FLOWABLE_EXTENSIONS_NAMESPACE, CmmnXmlConstants.ATTRIBUTE_FALLBACK_TO_DEFAULT_TENANT); if (fallbackToDefaultTenantValue != null) { processTask.setFallbackToDefaultTenant(Boolean.parseBoolean(fallbackToDefaultTenantValue)); diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractChildTaskExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractChildTaskExport.java index d90c74482c3..52a28773b6c 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractChildTaskExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractChildTaskExport.java @@ -32,6 +32,9 @@ protected void writePlanItemDefinitionSpecificAttributes(T planItemDefinition, X if (planItemDefinition.isInheritBusinessKey()) { xtw.writeAttribute(FLOWABLE_EXTENSIONS_PREFIX, FLOWABLE_EXTENSIONS_NAMESPACE, ATTRIBUTE_INHERIT_BUSINESS_KEY, String.valueOf(planItemDefinition.isInheritBusinessKey())); } + if (planItemDefinition.isInheritVariables()) { + xtw.writeAttribute(FLOWABLE_EXTENSIONS_PREFIX, FLOWABLE_EXTENSIONS_NAMESPACE, ATTRIBUTE_INHERIT_VARIABLES, String.valueOf(planItemDefinition.isInheritVariables())); + } } @Override diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/CaseTaskCmmnXmlConverterTest.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/CaseTaskCmmnXmlConverterTest.java index ba068d43693..5673f927e53 100644 --- a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/CaseTaskCmmnXmlConverterTest.java +++ b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/CaseTaskCmmnXmlConverterTest.java @@ -52,6 +52,7 @@ public void validateModel(CmmnModel cmmnModel) { assertThat(task1.getCaseRefExpression()).isEqualTo("caseDefinitionKey"); assertThat(task1.getFallbackToDefaultTenant()).isTrue(); assertThat(task1.isSameDeployment()).isTrue(); + assertThat(task1.isInheritVariables()).isTrue(); assertThat(task1.getInParameters()) .extracting(IOParameter::getSource, IOParameter::getTarget) diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ProcessTask2CmmnXmlConverterTest.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ProcessTask2CmmnXmlConverterTest.java index f17798afaa8..3d9085494e5 100644 --- a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ProcessTask2CmmnXmlConverterTest.java +++ b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ProcessTask2CmmnXmlConverterTest.java @@ -49,6 +49,7 @@ public void validateModel(CmmnModel cmmnModel) { .isInstanceOfSatisfying(ProcessTask.class, task1 -> { assertThat(task1.getProcessRefExpression()).isEqualTo("myTestProcess"); assertThat((task1.isSameDeployment())).isTrue(); + assertThat(task1.isInheritVariables()).isTrue(); assertThat(task1.getInParameters()).isEmpty(); assertThat(task1.getOutParameters()).isEmpty(); diff --git a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/case-task.cmmn b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/case-task.cmmn index c3bc7d5baff..39e9965347f 100644 --- a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/case-task.cmmn +++ b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/case-task.cmmn @@ -21,7 +21,7 @@ - + diff --git a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/process-task2.cmmn b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/process-task2.cmmn index 2a4754d68f4..31dcabbacef 100644 --- a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/process-task2.cmmn +++ b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/process-task2.cmmn @@ -17,7 +17,7 @@ - + diff --git a/modules/flowable-cmmn-engine-configurator/src/test/java/org/flowable/cmmn/test/ProcessTaskTest.java b/modules/flowable-cmmn-engine-configurator/src/test/java/org/flowable/cmmn/test/ProcessTaskTest.java index aa6f07e8267..d7edbbc7869 100644 --- a/modules/flowable-cmmn-engine-configurator/src/test/java/org/flowable/cmmn/test/ProcessTaskTest.java +++ b/modules/flowable-cmmn-engine-configurator/src/test/java/org/flowable/cmmn/test/ProcessTaskTest.java @@ -1015,6 +1015,33 @@ public void testIOParameterCombinations() { .hasSize(4 + 4); // 4 from in mapping, 4 from task complete } + @Test + @CmmnDeployment + @org.flowable.engine.test.Deployment(resources = "org/flowable/cmmn/test/oneTaskProcess.bpmn20.xml") + public void testProcessTaskInheritVariables() { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("myCase") + .variable("caseVariableA", "hello") + .variable("caseVariableB", "world") + .variable("caseVariableC", 42) + .start(); + + PlanItemInstance processTaskPlanItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .planItemDefinitionType(PlanItemDefinitionType.PROCESS_TASK) + .singleResult(); + String processInstanceId = processTaskPlanItemInstance.getReferenceId(); + ProcessInstance processInstance = processEngineRuntimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + assertThat(processInstance).isNotNull(); + + // All case variables are inherited into the child process instance (like a BPMN call activity) + assertThat(processEngineRuntimeService.getVariable(processInstanceId, "caseVariableA")).isEqualTo("hello"); + assertThat(processEngineRuntimeService.getVariable(processInstanceId, "caseVariableC")).isEqualTo(42); + + // An explicit in parameter takes precedence over the inherited variable + assertThat(processEngineRuntimeService.getVariable(processInstanceId, "caseVariableB")).isEqualTo("overridden"); + } + @Test @CmmnDeployment public void testProcessTaskWithSkipExpressions() { diff --git a/modules/flowable-cmmn-engine-configurator/src/test/resources/org/flowable/cmmn/test/ProcessTaskTest.testProcessTaskInheritVariables.cmmn b/modules/flowable-cmmn-engine-configurator/src/test/resources/org/flowable/cmmn/test/ProcessTaskTest.testProcessTaskInheritVariables.cmmn new file mode 100644 index 00000000000..7062df23cc3 --- /dev/null +++ b/modules/flowable-cmmn-engine-configurator/src/test/resources/org/flowable/cmmn/test/ProcessTaskTest.testProcessTaskInheritVariables.cmmn @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/CaseTaskActivityBehavior.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/CaseTaskActivityBehavior.java index f62dbd9eb36..0d1bcfeb442 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/CaseTaskActivityBehavior.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/CaseTaskActivityBehavior.java @@ -57,7 +57,8 @@ public class CaseTaskActivityBehavior extends ChildTaskActivityBehavior implemen protected CaseTask caseTask; public CaseTaskActivityBehavior(Expression caseRefExpression, CaseTask caseTask) { - super(caseTask.isBlocking(), caseTask.getBlockingExpression(), caseTask.getInParameters(), caseTask.getOutParameters()); + super(caseTask.isBlocking(), caseTask.getBlockingExpression(), caseTask.getInParameters(), caseTask.getOutParameters(), + caseTask.isInheritVariables()); this.caseRefExpression = caseRefExpression; this.caseRef = caseTask.getCaseRef(); this.fallbackToDefaultTenant = caseTask.getFallbackToDefaultTenant(); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ChildTaskActivityBehavior.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ChildTaskActivityBehavior.java index 866bc0edd00..f9d12caf70e 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ChildTaskActivityBehavior.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ChildTaskActivityBehavior.java @@ -43,6 +43,7 @@ public abstract class ChildTaskActivityBehavior extends CoreCmmnTriggerableActiv protected String isBlockingExpression; protected List inParameters; protected List outParameters; + protected boolean inheritVariables; public ChildTaskActivityBehavior(boolean isBlocking, String isBlockingExpression) { this.isBlocking = isBlocking; @@ -50,9 +51,15 @@ public ChildTaskActivityBehavior(boolean isBlocking, String isBlockingExpression } public ChildTaskActivityBehavior(boolean isBlocking, String isBlockingExpression, List inParameters, List outParameters) { + this(isBlocking, isBlockingExpression, inParameters, outParameters, false); + } + + public ChildTaskActivityBehavior(boolean isBlocking, String isBlockingExpression, List inParameters, List outParameters, + boolean inheritVariables) { this(isBlocking, isBlockingExpression); this.inParameters = inParameters; this.outParameters = outParameters; + this.inheritVariables = inheritVariables; } @Override @@ -83,6 +90,12 @@ protected void handleInParameters(PlanItemInstanceEntity planItemInstanceEntity, CmmnEngineConfiguration cmmnEngineConfiguration, Map inParametersMap, ExpressionManager expressionManager) { + if (inheritVariables) { + // Inherit all variables from the parent. Explicit in parameters are + // applied afterwards, so they take precedence over the inherited variables. + inParametersMap.putAll(planItemInstanceEntity.getVariables()); + } + IOParameterUtil.processInParameters(inParameters, planItemInstanceEntity, inParametersMap, expressionManager); } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ProcessTaskActivityBehavior.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ProcessTaskActivityBehavior.java index c47331e67fa..7119c012ede 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ProcessTaskActivityBehavior.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ProcessTaskActivityBehavior.java @@ -57,7 +57,8 @@ public class ProcessTaskActivityBehavior extends ChildTaskActivityBehavior imple protected ProcessTask processTask; public ProcessTaskActivityBehavior(Process process, Expression processRefExpression, ProcessTask processTask) { - super(processTask.isBlocking(), processTask.getBlockingExpression(), processTask.getInParameters(), processTask.getOutParameters()); + super(processTask.isBlocking(), processTask.getBlockingExpression(), processTask.getInParameters(), processTask.getOutParameters(), + processTask.isInheritVariables()); this.process = process; this.processRefExpression = processRefExpression; this.processRef = processTask.getProcessRef(); diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseTaskTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseTaskTest.java index 8ff291a8626..bd24368a082 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseTaskTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseTaskTest.java @@ -1236,6 +1236,32 @@ public void testWithInheritBusinessKeyButWithoutBusinessKey() { cmmnRuntimeService.triggerPlanItemInstance(planItemInstance.getId()); } + @Test + @CmmnDeployment(resources = { + "org/flowable/cmmn/test/runtime/CaseTaskTest.testCaseTaskInheritVariables.cmmn", + "org/flowable/cmmn/test/runtime/oneTaskCase.cmmn" + }) + public void testCaseTaskInheritVariables() { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("myCase") + .variable("caseVariableA", "hello") + .variable("caseVariableB", "world") + .variable("caseVariableC", 42) + .start(); + + CaseInstance subCase = cmmnRuntimeService.createCaseInstanceQuery() + .caseDefinitionKey("oneTaskCase") + .singleResult(); + assertThat(subCase).isNotNull(); + + // All parent case variables are inherited into the child case instance (like a BPMN call activity) + assertThat(cmmnRuntimeService.getVariable(subCase.getId(), "caseVariableA")).isEqualTo("hello"); + assertThat(cmmnRuntimeService.getVariable(subCase.getId(), "caseVariableC")).isEqualTo(42); + + // An explicit in parameter takes precedence over the inherited variable + assertThat(cmmnRuntimeService.getVariable(subCase.getId(), "caseVariableB")).isEqualTo("overridden"); + } + @Test @CmmnDeployment public void testIdVariableName() { diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/CaseTaskTest.testCaseTaskInheritVariables.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/CaseTaskTest.testCaseTaskInheritVariables.cmmn new file mode 100644 index 00000000000..adaeedb0ffd --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/CaseTaskTest.testCaseTaskInheritVariables.cmmn @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ChildTask.java b/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ChildTask.java index 59f705488ba..e7017b1c0d5 100644 --- a/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ChildTask.java +++ b/modules/flowable-cmmn-model/src/main/java/org/flowable/cmmn/model/ChildTask.java @@ -22,6 +22,7 @@ public class ChildTask extends Task implements HasInParameters, HasOutParameters protected String businessKey; protected boolean inheritBusinessKey; + protected boolean inheritVariables; protected List inParameters = new ArrayList<>(); protected List outParameters = new ArrayList<>(); @@ -41,6 +42,14 @@ public void setInheritBusinessKey(boolean inheritBusinessKey) { this.inheritBusinessKey = inheritBusinessKey; } + public boolean isInheritVariables() { + return inheritVariables; + } + + public void setInheritVariables(boolean inheritVariables) { + this.inheritVariables = inheritVariables; + } + @Override public List getInParameters() { return inParameters;