Skip to content

Commit

Permalink
JBPM-5681 - DMN support for BusinessRuleTask (#746)
Browse files Browse the repository at this point in the history
  • Loading branch information
mswiderski committed Feb 15, 2017
1 parent 497c491 commit c73c280
Show file tree
Hide file tree
Showing 12 changed files with 1,021 additions and 38 deletions.
6 changes: 6 additions & 0 deletions jbpm-bpmn2/pom.xml
Expand Up @@ -65,6 +65,7 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>


<!-- Test -->
<dependency>
Expand All @@ -77,6 +78,11 @@
<artifactId>xmlunit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-dmn-core</artifactId>
<scope>test</scope>
</dependency>

<!-- test persistence -->
<dependency>
Expand Down
Expand Up @@ -37,6 +37,9 @@

public class BusinessRuleTaskHandler extends AbstractNodeHandler {

private static final String NAMESPACE_PROP = "namespace";
private static final String MODEL_PROP = "model";
private static final String DECISION_PROP = "decision";
private DataTransformerRegistry transformerRegistry = DataTransformerRegistry.get();

protected Node createNode(Attributes attrs) {
Expand All @@ -56,6 +59,12 @@ protected void handleNode(final Node node, final Element element, final String u
if (ruleFlowGroup != null) {
ruleSetNode.setRuleFlowGroup(ruleFlowGroup);
}
String language = element.getAttribute("implementation");
if (language == null || language.equalsIgnoreCase("##unspecified") || language.isEmpty()) {
language = RuleSetNode.DRL_LANG;
}
ruleSetNode.setLanguage(language);

org.w3c.dom.Node xmlNode = element.getFirstChild();
while (xmlNode != null) {
String nodeName = xmlNode.getNodeName();
Expand All @@ -68,6 +77,9 @@ protected void handleNode(final Node node, final Element element, final String u
}
xmlNode = xmlNode.getNextSibling();
}
ruleSetNode.setNamespace((String) ruleSetNode.removeParameter(NAMESPACE_PROP));
ruleSetNode.setModel((String) ruleSetNode.removeParameter(MODEL_PROP));
ruleSetNode.setDecision((String) ruleSetNode.removeParameter(DECISION_PROP));

handleScript(ruleSetNode, element, "onEntry");
handleScript(ruleSetNode, element, "onExit");
Expand All @@ -77,8 +89,11 @@ public void writeNode(Node node, StringBuilder xmlDump, int metaDataType) {
RuleSetNode ruleSetNode = (RuleSetNode) node;
writeNode("businessRuleTask", ruleSetNode, xmlDump, metaDataType);
if (ruleSetNode.getRuleFlowGroup() != null) {
xmlDump.append("g:ruleFlowGroup=\"" + XmlBPMNProcessDumper.replaceIllegalCharsAttribute(ruleSetNode.getRuleFlowGroup()) + "\" >" + EOL);
xmlDump.append("g:ruleFlowGroup=\"" + XmlBPMNProcessDumper.replaceIllegalCharsAttribute(ruleSetNode.getRuleFlowGroup()) + "\" " + EOL);
}

xmlDump.append(" implementation=\"" + XmlBPMNProcessDumper.replaceIllegalCharsAttribute(ruleSetNode.getLanguage()) + "\" >" + EOL);

writeExtensionElements(ruleSetNode, xmlDump);
writeIO(ruleSetNode, xmlDump);
endNode("businessRuleTask", xmlDump);
Expand Down
87 changes: 87 additions & 0 deletions jbpm-bpmn2/src/test/java/org/jbpm/bpmn2/ActivityTest.java
Expand Up @@ -74,6 +74,8 @@

import javax.naming.InitialContext;
import javax.transaction.UserTransaction;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -1838,4 +1840,89 @@ public void testCallActivityWithDataAssignment() throws Exception {
assertProcessInstanceCompleted(processInstance);
assertEquals("Hello Genworth welcome to jBPMS!", ((WorkflowProcessInstance) processInstance).getVariable("message"));
}

@Test
public void testDMNBusinessRuleTask()throws Exception {
KieBase kbase = createKnowledgeBaseWithoutDumper(
"dmn/BPMN2-BusinessRuleTaskDMN.bpmn2", "dmn/0020-vacation-days.dmn");
ksession = createKnowledgeSession(kbase);
// first run 16, 1 and expected days is 27
Map<String, Object> params = new HashMap<String, Object>();
params.put("age", 16);
params.put("yearsOfService", 1);
ProcessInstance processInstance = ksession.startProcess("BPMN2-BusinessRuleTask", params);

assertProcessInstanceFinished(processInstance, ksession);
BigDecimal vacationDays = (BigDecimal) ((WorkflowProcessInstance) processInstance).getVariable("vacationDays");
assertEquals(BigDecimal.valueOf(27), vacationDays);

// second run 44, 20 and expected days is 24
params = new HashMap<String, Object>();
params.put("age", 44);
params.put("yearsOfService", 20);
processInstance = ksession.startProcess("BPMN2-BusinessRuleTask", params);

assertProcessInstanceFinished(processInstance, ksession);
vacationDays = (BigDecimal) ((WorkflowProcessInstance) processInstance).getVariable("vacationDays");
assertEquals(BigDecimal.valueOf(24), vacationDays);

// second run 50, 30 and expected days is 30
params = new HashMap<String, Object>();
params.put("age", 50);
params.put("yearsOfService", 30);
processInstance = ksession.startProcess("BPMN2-BusinessRuleTask", params);

assertProcessInstanceFinished(processInstance, ksession);
vacationDays = (BigDecimal) ((WorkflowProcessInstance) processInstance).getVariable("vacationDays");
assertEquals(BigDecimal.valueOf(30), vacationDays);
}

@Test
public void testDMNBusinessRuleTaskByDecisionName()throws Exception {
KieBase kbase = createKnowledgeBaseWithoutDumper(
"dmn/BPMN2-BusinessRuleTaskDMNByDecisionName.bpmn2", "dmn/0020-vacation-days.dmn");
ksession = createKnowledgeSession(kbase);
// first run 16, 1 and expected days is 5
Map<String, Object> params = new HashMap<String, Object>();
params.put("age", 16);
params.put("yearsOfService", 1);
ProcessInstance processInstance = ksession.startProcess("BPMN2-BusinessRuleTask", params);

assertProcessInstanceFinished(processInstance, ksession);
BigDecimal vacationDays = (BigDecimal) ((WorkflowProcessInstance) processInstance).getVariable("vacationDays");
assertEquals(BigDecimal.valueOf(5), vacationDays);
}

@Test
public void testDMNBusinessRuleTaskMultipleDecisionsOutput()throws Exception {
KieBase kbase = createKnowledgeBaseWithoutDumper(
"dmn/BPMN2-BusinessRuleTaskDMNMultipleDecisionsOutput.bpmn2", "dmn/0020-vacation-days.dmn");
ksession = createKnowledgeSession(kbase);
// first run 16, 1 and expected days is 5
Map<String, Object> params = new HashMap<String, Object>();
params.put("age", 16);
params.put("yearsOfService", 1);
ProcessInstance processInstance = ksession.startProcess("BPMN2-BusinessRuleTask", params);

assertProcessInstanceFinished(processInstance, ksession);
BigDecimal vacationDays = (BigDecimal) ((WorkflowProcessInstance) processInstance).getVariable("vacationDays");
assertEquals(BigDecimal.valueOf(27), vacationDays);
BigDecimal extraDays = (BigDecimal) ((WorkflowProcessInstance) processInstance).getVariable("extraDays");
assertEquals(BigDecimal.valueOf(5), extraDays);
}

@Test
public void testDMNBusinessRuleTaskInvalidExecution()throws Exception {
KieBase kbase = createKnowledgeBaseWithoutDumper(
"dmn/BPMN2-BusinessRuleTaskDMNByDecisionName.bpmn2", "dmn/0020-vacation-days.dmn");
ksession = createKnowledgeSession(kbase);
Map<String, Object> params = new HashMap<String, Object>();
params.put("age", 16);

try {
ksession.startProcess("BPMN2-BusinessRuleTask", params);
} catch (Exception e) {
assertTrue(e.getMessage().contains("DMN result errors"));
}
}
}
190 changes: 190 additions & 0 deletions jbpm-bpmn2/src/test/resources/dmn/0020-vacation-days.dmn
@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="_0020_vacation_days" name="0020-vacation-days"
namespace="https://www.drools.org/kie-dmn" xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd"
xmlns:kie="https://www.drools.org/kie-dmn" xmlns:feel="http://www.omg.org/spec/FEEL/20140401">

<inputData id="i_Age" name="Age">
<variable name="Age" typeRef="feel:number" />
</inputData>

<inputData id="i_Years_of_Service" name="Years of Service">
<variable name="Years of Service" typeRef="feel:number" />
</inputData>

<decision name="Total Vacation Days" id="d_Total_Vacation_Days">
<variable name="Total Vacation Days" typeRef="feel:number" />
<informationRequirement>
<requiredDecision href="#d_Base_Vacation_Days" />
</informationRequirement>
<informationRequirement>
<requiredDecision href="#d_Extra_days_case_1" />
</informationRequirement>
<informationRequirement>
<requiredDecision href="#d_Extra_days_case_2" />
</informationRequirement>
<informationRequirement>
<requiredDecision href="#d_Extra_days_case_3" />
</informationRequirement>
<literalExpression>
<text>Base Vacation Days +
max( Extra days case 1, Extra days case 3 ) +
Extra days case 2
</text>
</literalExpression>
</decision>

<decision name="Extra days case 1" id="d_Extra_days_case_1">
<variable name="Extra days case 1" typeRef="feel:number" />
<informationRequirement>
<requiredInput href="#i_Age" />
</informationRequirement>
<informationRequirement>
<requiredInput href="#i_Years_of_Service" />
</informationRequirement>
<decisionTable hitPolicy="COLLECT" aggregation="MAX">
<input id="d_Extra_days_case_1_dt_i_age" label="Age">
<inputExpression typeRef="feel:number">
<text>Age</text>
</inputExpression>
</input>
<input id="d_Extra_days_case_1_dt_i_years" label="Years of Service">
<inputExpression typeRef="feel:number">
<text>Years of Service</text>
</inputExpression>
</input>
<output id="d_Extra_days_case_1_dt_o" label="Extra days">
<defaultOutputEntry>
<text>0</text>
</defaultOutputEntry>
</output>
<rule id="d_Extra_days_case_1_dt_r1">
<inputEntry id="d_Extra_days_case_1_dt_r1_i1">
<text>&lt;18,&gt;=60</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_1_dt_r1_i2">
<text>-</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_1_dt_r1_o1">
<text>5</text>
</outputEntry>
</rule>
<rule id="d_Extra_days_case_1_dt_r2">
<inputEntry id="d_Extra_days_case_1_dt_r2_i1">
<text>-</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_1_dt_r2_i2">
<text>&gt;=30</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_1_dt_r2_o1">
<text>5</text>
</outputEntry>
</rule>
</decisionTable>
</decision>

<decision name="Extra days case 2" id="d_Extra_days_case_2">
<variable name="Extra days case 2" typeRef="feel:number" />
<informationRequirement>
<requiredInput href="#i_Age" />
</informationRequirement>
<informationRequirement>
<requiredInput href="#i_Years_of_Service" />
</informationRequirement>
<decisionTable hitPolicy="COLLECT" aggregation="MAX">
<input id="d_Extra_days_case_2_dt_i_age" label="Age">
<inputExpression typeRef="feel:number">
<text>Age</text>
</inputExpression>
</input>
<input id="d_Extra_days_case_2_dt_i_years" label="Years of Service">
<inputExpression typeRef="feel:number">
<text>Years of Service</text>
</inputExpression>
</input>
<output id="d_Extra_days_case_2_dt_o" label="Extra days">
<defaultOutputEntry>
<text>0</text>
</defaultOutputEntry>
</output>
<rule id="d_Extra_days_case_2_dt_r1">
<inputEntry id="d_Extra_days_case_2_dt_r1_i1">
<text>-</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_2_dt_r1_i2">
<text>&gt;=30</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_2_dt_r1_o1">
<text>3</text>
</outputEntry>
</rule>
<rule id="d_Extra_days_case_2_dt_r2">
<inputEntry id="d_Extra_days_case_2_dt_r2_i1">
<text>&gt;=60</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_2_dt_r2_i2">
<text>-</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_2_dt_r2_o1">
<text>3</text>
</outputEntry>
</rule>
</decisionTable>
</decision>

<decision name="Extra days case 3" id="d_Extra_days_case_3">
<variable name="Extra days case 3" typeRef="feel:number" />
<informationRequirement>
<requiredInput href="#i_Age" />
</informationRequirement>
<informationRequirement>
<requiredInput href="#i_Years_of_Service" />
</informationRequirement>
<decisionTable hitPolicy="COLLECT" aggregation="MAX">
<input id="d_Extra_days_case_3_dt_i_age" label="Age">
<inputExpression typeRef="feel:number">
<text>Age</text>
</inputExpression>
</input>
<input id="d_Extra_days_case_3_dt_i_years" label="Years of Service">
<inputExpression typeRef="feel:number">
<text>Years of Service</text>
</inputExpression>
</input>
<output id="d_Extra_days_case_3_dt_o" label="Extra days">
<defaultOutputEntry>
<text>0</text>
</defaultOutputEntry>
</output>
<rule id="d_Extra_days_case_3_dt_r1">
<inputEntry id="d_Extra_days_case_3_dt_r1_i1">
<text>-</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_3_dt_r1_i2">
<text>[15..30)</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_3_dt_r1_o1">
<text>2</text>
</outputEntry>
</rule>
<rule id="d_Extra_days_case_3_dt_r2">
<inputEntry id="d_Extra_days_case_3_dt_r2_i1">
<text>&gt;=45</text>
</inputEntry>
<inputEntry id="d_Extra_days_case_3_dt_r2_i2">
<text>-</text>
</inputEntry>
<outputEntry id="d_Extra_days_case_3_dt_r2_o1">
<text>2</text>
</outputEntry>
</rule>
</decisionTable>
</decision>

<decision id="d_Base_Vacation_Days" name="Base Vacation Days">
<variable name="Base Vacation Days" typeRef="feel:number" />
<literalExpression>
<text>22</text>
</literalExpression>
</decision>

</definitions>

0 comments on commit c73c280

Please sign in to comment.