From 49987ff1a26a8bf917a2231da7a5f5d37413ecb2 Mon Sep 17 00:00:00 2001 From: egonzale Date: Mon, 7 Jun 2021 09:34:37 +0200 Subject: [PATCH] [JBPM-9774] Incorrect object type returned in a DMN business-rule task of a BPMN --- .../test/functional/task/RuleTaskTest.java | 26 ++++++ jbpm-workitems/jbpm-workitems-bpmn2/pom.xml | 5 +- .../bpmn2/AbstractRuleTaskHandler.java | 89 ++++++++++++++++--- .../bpmn2/BusinessRuleTaskHandler.java | 10 +++ .../workitem/bpmn2/DecisionTaskHandler.java | 11 ++- .../workitem/bpmn2/BusinessRuleTaskTest.java | 2 +- 6 files changed, 127 insertions(+), 16 deletions(-) diff --git a/jbpm-test-coverage/src/test/java/org/jbpm/test/functional/task/RuleTaskTest.java b/jbpm-test-coverage/src/test/java/org/jbpm/test/functional/task/RuleTaskTest.java index 5eea151bdb..b5f3e8722e 100644 --- a/jbpm-test-coverage/src/test/java/org/jbpm/test/functional/task/RuleTaskTest.java +++ b/jbpm-test-coverage/src/test/java/org/jbpm/test/functional/task/RuleTaskTest.java @@ -21,9 +21,11 @@ import java.util.List; import java.util.Map; +import org.jbpm.process.workitem.bpmn2.BusinessRuleTaskHandler; import org.jbpm.test.JbpmTestCase; import org.jbpm.test.listener.IterableProcessEventListener; import org.junit.Test; +import org.kie.api.KieBase; import org.kie.api.command.Command; import org.kie.api.io.ResourceType; import org.kie.api.runtime.KieSession; @@ -59,6 +61,30 @@ public RuleTaskTest() { super(false); } + @Test + public void testBusinessRuleTaskProcess() throws Exception { + KieBase kbase = readKnowledgeBase(); + KieSession ksession = createSession(kbase); + + BusinessRuleTaskHandler handler = new BusinessRuleTaskHandler(GROUP_ID, + ARTIFACT_ID, + VERSION); + ksession.getWorkItemManager().registerWorkItemHandler("BusinessRuleTask", + handler); + Map params = new HashMap(); + params.put("person", + new org.jbpm.process.workitem.bpmn2.objects.Person("john")); + + WorkflowProcessInstance processInstance = (WorkflowProcessInstance) ksession.startProcess("evaluation.ruletask", + params); + org.jbpm.process.workitem.bpmn2.objects.Person variable = (org.jbpm.process.workitem.bpmn2.objects.Person) processInstance.getVariable("person"); + assertEquals("john", + variable.getName()); + assertEquals(35, + variable.getAge().intValue()); + assertEquals(ProcessInstance.STATE_COMPLETED, + processInstance.getState()); + } @Test(timeout = 30000) public void testRuleTask() { Map res = new HashMap(); diff --git a/jbpm-workitems/jbpm-workitems-bpmn2/pom.xml b/jbpm-workitems/jbpm-workitems-bpmn2/pom.xml index 4441a0e343..3d6387c69e 100644 --- a/jbpm-workitems/jbpm-workitems-bpmn2/pom.xml +++ b/jbpm-workitems/jbpm-workitems-bpmn2/pom.xml @@ -117,7 +117,10 @@ - + + com.fasterxml.jackson.core + jackson-databind + org.jbpm jbpm-workitems-core diff --git a/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/AbstractRuleTaskHandler.java b/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/AbstractRuleTaskHandler.java index d1031b93b2..42519af4ac 100644 --- a/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/AbstractRuleTaskHandler.java +++ b/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/AbstractRuleTaskHandler.java @@ -15,6 +15,7 @@ */ package org.jbpm.process.workitem.bpmn2; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -23,12 +24,10 @@ import java.util.stream.Collectors; import org.jbpm.process.workitem.core.AbstractLogOrThrowWorkItemHandler; -import org.jbpm.process.workitem.core.util.Wid; -import org.jbpm.process.workitem.core.util.WidMavenDepends; -import org.jbpm.process.workitem.core.util.WidParameter; -import org.jbpm.process.workitem.core.util.service.WidAction; -import org.jbpm.process.workitem.core.util.service.WidAuth; -import org.jbpm.process.workitem.core.util.service.WidService; +import org.jbpm.workflow.core.node.DataAssociation; +import org.jbpm.workflow.core.node.WorkItemNode; +import org.jbpm.workflow.instance.WorkflowProcessInstance; +import org.jbpm.workflow.instance.node.WorkItemNodeInstance; import org.kie.api.KieServices; import org.kie.api.builder.KieScanner; import org.kie.api.command.BatchExecutionCommand; @@ -38,6 +37,8 @@ import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.StatelessKieSession; +import org.kie.api.runtime.manager.RuntimeEngine; +import org.kie.api.runtime.manager.RuntimeManager; import org.kie.api.runtime.process.WorkItem; import org.kie.api.runtime.process.WorkItemManager; import org.kie.api.runtime.rule.FactHandle; @@ -47,9 +48,13 @@ import org.kie.dmn.api.core.DMNResult; import org.kie.dmn.api.core.DMNRuntime; import org.kie.internal.runtime.Cacheable; +import org.kie.internal.runtime.manager.RuntimeManagerRegistry; +import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; + /** * Additional BusinessRuleTask support that allows to decouple rules from processes - as default BusinessRuleTask * uses exact same working memory (kie session) as process which essentially means same kbase. @@ -90,6 +95,9 @@ public abstract class AbstractRuleTaskHandler extends AbstractLogOrThrowWorkItem private KieContainer kieContainer; private KieScanner kieScanner; + private ClassLoader classLoader; + private RuntimeManager runtimeManager; + public AbstractRuleTaskHandler(String groupId, String artifactId, String version) { @@ -99,10 +107,18 @@ public AbstractRuleTaskHandler(String groupId, -1); } + public AbstractRuleTaskHandler(String groupId, + String artifactId, + String version, + long scannerInterval) { + this(groupId, artifactId, version, scannerInterval, null, null); + } public AbstractRuleTaskHandler(String groupId, String artifactId, String version, - long scannerInterval) { + long scannerInterval, + ClassLoader classLoader, + RuntimeManager runtimeManager) { logger.debug("About to create KieContainer for {}, {}, {} with scanner interval {}", groupId, artifactId, @@ -111,7 +127,8 @@ public AbstractRuleTaskHandler(String groupId, kieContainer = kieServices.newKieContainer(kieServices.newReleaseId(groupId, artifactId, version)); - + this.classLoader = classLoader; + this.runtimeManager = runtimeManager; if (scannerInterval > 0) { kieScanner = kieServices.newKieScanner(kieContainer); kieScanner.start(scannerInterval); @@ -154,7 +171,8 @@ public void executeWorkItem(WorkItem workItem, results); } } else if (DMN_LANG.equalsIgnoreCase(language)) { - handleDMN(parameters, + handleDMN(workItem, + parameters, results); } else { throw new IllegalArgumentException("Not supported language type " + language); @@ -218,8 +236,7 @@ protected void handleStateless(WorkItem workItem, String kieSessionName, Map parameters, Map results) { - logger.debug("Evaluating rules in stateless session with name {}", - kieSessionName); + logger.debug("Evaluating rules in stateless session with name {}", kieSessionName); StatelessKieSession kieSession = kieContainer.newStatelessKieSession(kieSessionName); List> commands = new ArrayList>(); @@ -246,7 +263,8 @@ protected void handleStateless(WorkItem workItem, } } - protected void handleDMN(Map parameters, + protected void handleDMN(WorkItem workItem, + Map parameters, Map results) { String namespace = (String) parameters.remove("Namespace"); String model = (String) parameters.remove("Model"); @@ -283,9 +301,54 @@ protected void handleDMN(Map parameters, throw new RuntimeException("DMN result errors:: " + errors); } - results.putAll(dmnResult.getContext().getAll()); + // no class loader defined we don't even try to convert. + if(classLoader == null || runtimeManager == null) { + results.putAll(dmnResult.getContext().getAll()); + return; + } + + RuntimeEngine engine = runtimeManager.getRuntimeEngine(ProcessInstanceIdContext.get(workItem.getProcessInstanceId())); + try { + KieSession localksession = engine.getKieSession(); + WorkflowProcessInstance processInstance = (WorkflowProcessInstance) + localksession.getProcessInstance(workItem.getProcessInstanceId()); + WorkItemNodeInstance nodeInstance = findNodeInstance(workItem.getId(), processInstance); + WorkItemNode workItemNode = (WorkItemNode) nodeInstance.getNode(); + // data outputs contains the structure refs for data association + Map dataTypeOutputs = (Map) workItemNode.getMetaData("DataOutputs"); + Map data = dmnResult.getContext().getAll(); + Map outcome = new HashMap<>(); + + if(!workItemNode.getOutAssociations().isEmpty()) { + // if there is one out association but the data is multiple we collapse to an object + if(workItemNode.getOutAssociations().size() == 1 && data.size() > 1) { + DataAssociation dataAssociation = workItemNode.getOutAssociations().get(0); + String type = dataTypeOutputs.get(dataAssociation.getTarget()); + outcome.put(dataAssociation.getTarget(), transformResult(data, type)); + } else { + // any other case we associated one by one + for(DataAssociation dataAssociation : workItemNode.getOutAssociations()) { + String type = dataTypeOutputs.get(dataAssociation.getTarget()); + outcome.put(dataAssociation.getTarget(), transformResult(data.get(dataAssociation.getTarget()), type)); + } + } + } else { + results.putAll(dmnResult.getContext().getAll()); + } + } catch (IOException | ClassNotFoundException e) { + results.putAll(dmnResult.getContext().getAll()); + } finally { + runtimeManager.disposeRuntimeEngine(engine); + } + + } + + private Object transformResult(Object toMarshal, String className) throws ClassNotFoundException, IOException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.convertValue(toMarshal, classLoader.loadClass(className)); } + public KieContainer getKieContainer() { return this.kieContainer; } diff --git a/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskHandler.java b/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskHandler.java index 2f1529e154..22315e0b4a 100644 --- a/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskHandler.java +++ b/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskHandler.java @@ -21,6 +21,7 @@ import org.jbpm.process.workitem.core.util.service.WidAction; import org.jbpm.process.workitem.core.util.service.WidAuth; import org.jbpm.process.workitem.core.util.service.WidService; +import org.kie.api.runtime.manager.RuntimeManager; /** * Additional BusinessRuleTask support that allows to decouple rules from processes - as default BusinessRuleTask @@ -72,6 +73,15 @@ public BusinessRuleTaskHandler(String groupId, super(groupId, artifactId, version, scannerInterval); } + public BusinessRuleTaskHandler(String groupId, + String artifactId, + String version, + long scannerInterval, + ClassLoader classLoader, + RuntimeManager runtimeManager) { + super(groupId, artifactId, version, scannerInterval, classLoader, runtimeManager); + } + @Override public String getRuleLanguage() { return DRL_LANG; diff --git a/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/DecisionTaskHandler.java b/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/DecisionTaskHandler.java index 3bd820be9b..d052f51065 100644 --- a/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/DecisionTaskHandler.java +++ b/jbpm-workitems/jbpm-workitems-bpmn2/src/main/java/org/jbpm/process/workitem/bpmn2/DecisionTaskHandler.java @@ -21,7 +21,7 @@ import org.jbpm.process.workitem.core.util.service.WidAction; import org.jbpm.process.workitem.core.util.service.WidAuth; import org.jbpm.process.workitem.core.util.service.WidService; - +import org.kie.api.runtime.manager.RuntimeManager; /** * Additional BusinessRuleTask support that allows to decouple rules from processes - as default BusinessRuleTask * uses exact same working memory (kie session) as process which essentially means same kbase. @@ -73,6 +73,15 @@ public DecisionTaskHandler(String groupId, super(groupId, artifactId, version, scannerInterval); } + public DecisionTaskHandler(String groupId, + String artifactId, + String version, + long scannerInterval, + ClassLoader classLoader, + RuntimeManager runtimeManager) { + super(groupId, artifactId, version, scannerInterval, classLoader, runtimeManager); + } + @Override public String getRuleLanguage() { return DMN_LANG; diff --git a/jbpm-workitems/jbpm-workitems-bpmn2/src/test/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskTest.java b/jbpm-workitems/jbpm-workitems-bpmn2/src/test/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskTest.java index 85535df99b..bf2e9ec446 100644 --- a/jbpm-workitems/jbpm-workitems-bpmn2/src/test/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskTest.java +++ b/jbpm-workitems/jbpm-workitems-bpmn2/src/test/java/org/jbpm/process/workitem/bpmn2/BusinessRuleTaskTest.java @@ -106,7 +106,7 @@ public void testDecisionTaskProcess() throws Exception { BusinessRuleTaskHandler handler = new BusinessRuleTaskHandler(GROUP_ID, ARTIFACT_ID, - VERSION); + VERSION,0); ksession.getWorkItemManager().registerWorkItemHandler("DecisionTask", handler); Map params = new HashMap();