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 de3b86b249 [Fix_#3465] Adds DMN support to SonataFlow (#3468)
de3b86b249 is described below

commit de3b86b249d36702fa10e2b48cafac7e0a8ed5dc
Author: Francisco Javier Tirado Sarti 
<[email protected]>
AuthorDate: Thu Apr 18 13:07:55 2024 +0200

    [Fix_#3465] Adds DMN support to SonataFlow (#3468)
    
    * [Fix_#3465] Adding DMNWorkItemHandler
    
    * [Fix_#3465] Node approach
    
    * [Fix #3465] Supporting white space
    
    * [Fix #3465] Refactor mapping
    
    * [Fix #3465]  Support function arguments
    
    * [Fix #3465] Gabrieles comment
---
 kogito-bom/pom.xml                                 |  22 +++++
 .../workflow/io/ClassPathContentLoader.java        |   4 +-
 .../workflow/io/URIContentLoaderFactory.java       |   4 +-
 .../workflow/parser/handlers/MappingSetter.java    |  26 ++++++
 .../workflow/parser/handlers/MappingUtils.java     |  74 +++++++++++++++
 .../serverless/workflow/utils/WorkItemBuilder.java |  78 +++++-----------
 .../workflow/io/URIContentLoaderTest.java          |   3 +-
 .../kogito-serverless-workflow-dmn-parser/pom.xml  |  62 +++++++++++++
 .../workflow/parser/types/DMNTypeHandler.java      | 103 +++++++++++++++++++++
 ....serverless.workflow.parser.FunctionTypeHandler |   1 +
 .../kogito/serverless/workflow/dmn/SWFDMNTest.java |  57 ++++++++++++
 .../kogito-serverless-workflow-dmn/pom.xml         |  24 +++++
 .../serverless/workflow/dmn/SWFDecisionEngine.java |  96 +++++++++++++++++++
 ...m.workflow.instance.rule.DecisionRuleTypeEngine |   1 +
 .../workflow/fluent/FunctionBuilder.java           |   9 +-
 .../kogito/serverless/workflow/SWFConstants.java   |   1 +
 kogito-serverless-workflow/pom.xml                 |   2 +
 17 files changed, 507 insertions(+), 60 deletions(-)

diff --git a/kogito-bom/pom.xml b/kogito-bom/pom.xml
index dc54a9d717..b07eec3f81 100755
--- a/kogito-bom/pom.xml
+++ b/kogito-bom/pom.xml
@@ -1941,6 +1941,28 @@
         <version>${project.version}</version>
         <classifier>sources</classifier>
       </dependency>
+      <dependency>
+        <groupId>org.kie.kogito</groupId>
+        <artifactId>kogito-serverless-workflow-dmn-parser</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.kie.kogito</groupId>
+        <artifactId>kogito-serverless-workflow-dmn-parser</artifactId>
+        <version>${project.version}</version>
+        <classifier>sources</classifier>
+      </dependency>
+      <dependency>
+        <groupId>org.kie.kogito</groupId>
+        <artifactId>kogito-serverless-workflow-dmn</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.kie.kogito</groupId>
+        <artifactId>kogito-serverless-workflow-dmn</artifactId>
+        <version>${project.version}</version>
+        <classifier>sources</classifier>
+      </dependency>
       <dependency>
         <groupId>org.kie.kogito</groupId>
         <artifactId>kogito-serverless-workflow-rest-parser</artifactId>
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java
index 56b80a8beb..413c199a67 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/ClassPathContentLoader.java
@@ -24,6 +24,8 @@ import java.io.UncheckedIOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
 import java.nio.file.Path;
 import java.util.Optional;
 
@@ -40,7 +42,7 @@ public class ClassPathContentLoader extends 
CachedContentLoader {
 
     static String getPath(URI uri) {
         final String classPathPrefix = "classpath:";
-        String str = uri.toString();
+        String str = URLDecoder.decode(uri.toString(), 
Charset.defaultCharset());
         if (str.toLowerCase().startsWith(classPathPrefix)) {
             str = str.substring(classPathPrefix.length());
             while (str.startsWith("/")) {
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java
index 002174e876..6e5b806594 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderFactory.java
@@ -23,6 +23,8 @@ import java.io.InputStream;
 import java.io.UncheckedIOException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
 import java.util.Optional;
 
 import org.kie.kogito.serverless.workflow.parser.ParserContext;
@@ -91,7 +93,7 @@ public class URIContentLoaderFactory {
     }
 
     public static Builder builder(String uri) {
-        return new Builder(URI.create(uri));
+        return new Builder(URI.create(URLEncoder.encode(uri, 
Charset.defaultCharset())));
     }
 
     public static class Builder {
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/MappingSetter.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/MappingSetter.java
new file mode 100644
index 0000000000..adfa6a4e62
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/MappingSetter.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.kogito.serverless.workflow.parser.handlers;
+
+public interface MappingSetter {
+
+    void accept(Object value);
+
+    void accept(String key, Object value);
+}
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/MappingUtils.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/MappingUtils.java
new file mode 100644
index 0000000000..ec64424831
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/MappingUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.kogito.serverless.workflow.parser.handlers;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.jbpm.ruleflow.core.factory.MappableNodeFactory;
+import org.kie.kogito.jackson.utils.JsonNodeVisitor;
+import org.kie.kogito.jackson.utils.JsonObjectUtils;
+import org.kie.kogito.serverless.workflow.SWFConstants;
+import org.kie.kogito.serverless.workflow.utils.ExpressionHandlerUtils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import io.serverlessworkflow.api.Workflow;
+
+public class MappingUtils {
+
+    public static <T extends MappableNodeFactory<?>> T addMapping(T 
nodeFactory, String inputVar, String outputVar) {
+        return (T) nodeFactory.inMapping(inputVar, 
SWFConstants.MODEL_WORKFLOW_VAR)
+                .outMapping(SWFConstants.RESULT, outputVar);
+    }
+
+    public static final void processArgs(Workflow workflow,
+            JsonNode functionArgs, MappingSetter setter) {
+        if (functionArgs.isObject()) {
+            functionsToMap(workflow, functionArgs).forEach((key, value) -> 
setter.accept(key, value));
+        } else {
+            Object object = functionReference(workflow, 
JsonObjectUtils.simpleToJavaValue(functionArgs));
+            setter.accept(object);
+        }
+    }
+
+    private static Map<String, Object> functionsToMap(Workflow workflow, 
JsonNode jsonNode) {
+        Map<String, Object> map = new LinkedHashMap<>();
+        if (jsonNode != null) {
+            Iterator<Entry<String, JsonNode>> iter = jsonNode.fields();
+            while (iter.hasNext()) {
+                Entry<String, JsonNode> entry = iter.next();
+                map.put(entry.getKey(), functionReference(workflow, 
JsonObjectUtils.simpleToJavaValue(entry.getValue())));
+            }
+        }
+        return map;
+    }
+
+    private static Object functionReference(Workflow workflow, Object object) {
+        if (object instanceof JsonNode) {
+            return JsonNodeVisitor.transformTextNode((JsonNode) object, node 
-> JsonObjectUtils.fromValue(ExpressionHandlerUtils.replaceExpr(workflow, 
node.asText())));
+        } else if (object instanceof CharSequence) {
+            return ExpressionHandlerUtils.replaceExpr(workflow, 
object.toString());
+        } else {
+            return object;
+        }
+    }
+}
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/WorkItemBuilder.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/WorkItemBuilder.java
index bd7b522b47..fbb03806de 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/WorkItemBuilder.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/WorkItemBuilder.java
@@ -19,20 +19,17 @@
 package org.kie.kogito.serverless.workflow.utils;
 
 import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 
 import org.jbpm.process.core.datatype.DataType;
 import org.jbpm.process.core.datatype.DataTypeResolver;
 import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory;
 import org.jbpm.ruleflow.core.factory.WorkItemNodeFactory;
-import org.kie.kogito.jackson.utils.JsonNodeVisitor;
-import org.kie.kogito.jackson.utils.JsonObjectUtils;
 import org.kie.kogito.process.expr.ExpressionHandlerFactory;
 import org.kie.kogito.serverless.workflow.SWFConstants;
 import org.kie.kogito.serverless.workflow.parser.ParserContext;
+import org.kie.kogito.serverless.workflow.parser.handlers.MappingSetter;
+import org.kie.kogito.serverless.workflow.parser.handlers.MappingUtils;
 import 
org.kie.kogito.serverless.workflow.suppliers.ExpressionParametersFactorySupplier;
 import org.kie.kogito.serverless.workflow.suppliers.ObjectResolverSupplier;
 
@@ -45,8 +42,6 @@ import io.serverlessworkflow.api.functions.FunctionRef;
 
 public abstract class WorkItemBuilder {
 
-    private static final String RESULT = "Result";
-
     protected <T extends RuleFlowNodeContainerFactory<T, ?>> 
WorkItemNodeFactory<T> addFunctionArgs(Workflow workflow, 
WorkItemNodeFactory<T> node, FunctionRef functionRef) {
         JsonNode functionArgs = functionRef.getArguments();
         if (functionArgs != null) {
@@ -72,59 +67,40 @@ public abstract class WorkItemBuilder {
             ParserContext parserContext,
             String inputVar,
             String outputVar) {
-        return embeddedSubProcess.workItemNode(parserContext.newId())
-                .inMapping(inputVar, SWFConstants.MODEL_WORKFLOW_VAR)
-                .outMapping(RESULT, outputVar);
+        return 
MappingUtils.addMapping(embeddedSubProcess.workItemNode(parserContext.newId()), 
inputVar, outputVar);
     }
 
     protected final void processArgs(Workflow workflow, WorkItemNodeFactory<?> 
workItemFactory,
             JsonNode functionArgs, String paramName) {
-        if (functionArgs.isObject()) {
-            functionsToMap(workflow, functionArgs).forEach((key, value) -> 
processArg(workflow, key, value, workItemFactory, paramName));
-        } else {
-            Object object = functionReference(workflow, 
JsonObjectUtils.simpleToJavaValue(functionArgs));
-            boolean isExpr = isExpression(workflow, object);
-            if (isExpr) {
-                workItemFactory.workParameterFactory(new 
ExpressionParametersFactorySupplier(workflow.getExpressionLang(), object, 
paramName));
-            } else {
-                workItemFactory.workParameter(SWFConstants.CONTENT_DATA, 
object);
+        MappingUtils.processArgs(workflow, functionArgs, new MappingSetter() {
+            @Override
+            public void accept(String key, Object value) {
+                boolean isExpr = isExpression(workflow, value);
+                workItemFactory
+                        .workParameter(key,
+                                isExpr ? new 
ObjectResolverSupplier(workflow.getExpressionLang(), value, paramName) : value)
+                        .workParameterDefinition(key,
+                                getDataType(value, isExpr));
             }
-            workItemFactory.workParameterDefinition(SWFConstants.CONTENT_DATA, 
getDataType(object, isExpr));
-        }
-    }
 
-    private Map<String, Object> functionsToMap(Workflow workflow, JsonNode 
jsonNode) {
-        Map<String, Object> map = new LinkedHashMap<>();
-        if (jsonNode != null) {
-            Iterator<Entry<String, JsonNode>> iter = jsonNode.fields();
-            while (iter.hasNext()) {
-                Entry<String, JsonNode> entry = iter.next();
-                map.put(entry.getKey(), functionReference(workflow, 
JsonObjectUtils.simpleToJavaValue(entry.getValue())));
+            @Override
+            public void accept(Object value) {
+                boolean isExpr = isExpression(workflow, value);
+                if (isExpr) {
+                    workItemFactory.workParameterFactory(new 
ExpressionParametersFactorySupplier(workflow.getExpressionLang(), value, 
paramName));
+                } else {
+                    workItemFactory.workParameter(SWFConstants.CONTENT_DATA, 
value);
+                }
+                
workItemFactory.workParameterDefinition(SWFConstants.CONTENT_DATA, 
getDataType(value, isExpr));
             }
-        }
-        return map;
+        });
     }
 
-    private Object functionReference(Workflow workflow, Object object) {
-        if (object instanceof JsonNode) {
-            return JsonNodeVisitor.transformTextNode((JsonNode) object, node 
-> JsonObjectUtils.fromValue(ExpressionHandlerUtils.replaceExpr(workflow, 
node.asText())));
-        } else if (object instanceof CharSequence) {
-            return ExpressionHandlerUtils.replaceExpr(workflow, 
object.toString());
-        } else {
-            return object;
-        }
-    }
-
-    private void processArg(Workflow workflow, String key, Object value, 
WorkItemNodeFactory<?> workItemFactory, String paramName) {
-        boolean isExpr = isExpression(workflow, value);
-        workItemFactory
-                .workParameter(key,
-                        isExpr ? new 
ObjectResolverSupplier(workflow.getExpressionLang(), value, paramName) : value)
-                .workParameterDefinition(key,
-                        getDataType(value, isExpr));
+    private static boolean isExpression(Workflow workflow, Object value) {
+        return value instanceof CharSequence && 
ExpressionHandlerFactory.get(workflow.getExpressionLang(), 
value.toString()).isValid() || value instanceof JsonNode;
     }
 
-    DataType getDataType(Object object, boolean isExpr) {
+    private static DataType getDataType(Object object, boolean isExpr) {
         if (object instanceof ObjectNode) {
             return DataTypeResolver.fromClass(Map.class);
         } else if (object instanceof ArrayNode) {
@@ -133,8 +109,4 @@ public abstract class WorkItemBuilder {
             return DataTypeResolver.fromObject(object, isExpr);
         }
     }
-
-    private boolean isExpression(Workflow workflow, Object value) {
-        return value instanceof CharSequence && 
ExpressionHandlerFactory.get(workflow.getExpressionLang(), 
value.toString()).isValid() || value instanceof JsonNode;
-    }
 }
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderTest.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderTest.java
index d3d9ac9b3d..e3c2ec3696 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderTest.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/io/URIContentLoaderTest.java
@@ -27,7 +27,6 @@ import 
org.kie.kogito.serverless.workflow.io.URIContentLoaderFactory.Builder;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static 
org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static 
org.kie.kogito.serverless.workflow.io.URIContentLoaderFactory.builder;
 import static 
org.kie.kogito.serverless.workflow.io.URIContentLoaderFactory.compoundURI;
 import static 
org.kie.kogito.serverless.workflow.io.URIContentLoaderFactory.readString;
@@ -53,7 +52,7 @@ class URIContentLoaderTest {
     @Test
     void testNotExistingClasspath() {
         Builder builder = builder("classpath:/noPepe.txt");
-        assertThatIllegalArgumentException().isThrownBy(() -> 
readString(builder));
+        assertThatExceptionOfType(UncheckedIOException.class).isThrownBy(() -> 
readString(builder));
     }
 
     @Test
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/pom.xml 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/pom.xml
new file mode 100644
index 0000000000..cbb756c105
--- /dev/null
+++ b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/pom.xml
@@ -0,0 +1,62 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.kie.kogito</groupId>
+    <artifactId>kogito-serverless-workflow</artifactId>
+    <version>999-SNAPSHOT</version>
+  </parent>
+   <name>Kogito :: Serverless Workflow :: DMN :: Parser</name>
+   
+  <properties>
+      
<java.module.name>org.kie.kogito.serverless.workflow.dmn.parser</java.module.name>
+  </properties>
+
+  <artifactId>kogito-serverless-workflow-dmn-parser</artifactId>
+  <dependencies>
+    <dependency>
+      <groupId>org.kie.kogito</groupId>
+      <artifactId>kogito-serverless-workflow-builder</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.kie.kogito</groupId>
+      <artifactId>kogito-serverless-workflow-dmn</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-params</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.kie.kogito</groupId>
+      <artifactId>kogito-serverless-workflow-fluent</artifactId>
+      <scope>test</scope>
+    </dependency>
+     <dependency>
+      <groupId>org.kie.kogito</groupId>
+      <artifactId>kogito-serverless-workflow-executor-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.kie</groupId>
+        <artifactId>kie-dmn-test-resources</artifactId>
+        <classifier>tests</classifier>
+        <version>${project.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.assertj</groupId>
+      <artifactId>assertj-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/main/java/org/kie/kogito/serverless/workflow/parser/types/DMNTypeHandler.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/main/java/org/kie/kogito/serverless/workflow/parser/types/DMNTypeHandler.java
new file mode 100644
index 0000000000..0b98f50dc2
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/main/java/org/kie/kogito/serverless/workflow/parser/types/DMNTypeHandler.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.kogito.serverless.workflow.parser.types;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.util.Map;
+import java.util.Objects;
+
+import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory;
+import org.jbpm.ruleflow.core.factory.NodeFactory;
+import org.jbpm.ruleflow.core.factory.RuleSetNodeFactory;
+import org.kie.kogito.decision.DecisionModel;
+import org.kie.kogito.dmn.DMNKogito;
+import org.kie.kogito.dmn.DmnDecisionModel;
+import org.kie.kogito.serverless.workflow.SWFConstants;
+import org.kie.kogito.serverless.workflow.dmn.SWFDecisionEngine;
+import org.kie.kogito.serverless.workflow.io.URIContentLoaderFactory;
+import org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler;
+import org.kie.kogito.serverless.workflow.parser.ParserContext;
+import org.kie.kogito.serverless.workflow.parser.VariableInfo;
+import org.kie.kogito.serverless.workflow.parser.handlers.MappingSetter;
+import org.kie.kogito.serverless.workflow.parser.handlers.MappingUtils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import io.serverlessworkflow.api.Workflow;
+import io.serverlessworkflow.api.functions.FunctionDefinition;
+import io.serverlessworkflow.api.functions.FunctionRef;
+
+public class DMNTypeHandler implements FunctionTypeHandler {
+
+    private static final String DMN_TYPE = "dmn";
+    public static final String NAMESPACE = "namespace";
+    public static final String MODEL = "model";
+    public static final String FILE = "file";
+
+    private static final String REQUIRED_MESSAGE = "%s is required on metadata 
for DMN";
+
+    @Override
+    public String type() {
+        return DMN_TYPE;
+    }
+
+    @Override
+    public boolean isCustom() {
+        return true;
+    }
+
+    @Override
+    public NodeFactory<?, ?> getActionNode(Workflow workflow, ParserContext 
context,
+            RuleFlowNodeContainerFactory<?, ?> embeddedSubProcess, 
FunctionDefinition functionDef,
+            FunctionRef functionRef, VariableInfo varInfo) {
+        Map<String, String> metadata = 
Objects.requireNonNull(functionDef.getMetadata(), "Metadata is required for 
DMN");
+        String namespace = Objects.requireNonNull(metadata.get(NAMESPACE), 
String.format(REQUIRED_MESSAGE, NAMESPACE));
+        String model = Objects.requireNonNull(metadata.get(MODEL), 
String.format(REQUIRED_MESSAGE, MODEL));
+        String file = Objects.requireNonNull(metadata.get(FILE), 
String.format(REQUIRED_MESSAGE, FILE));
+        RuleSetNodeFactory<?> nodeFactory = 
MappingUtils.addMapping(embeddedSubProcess.ruleSetNode(context.newId()).decision(namespace,
 model, model, () -> loadDMNFromFile(namespace, model, file)),
+                varInfo.getInputVar(), varInfo.getOutputVar());
+        JsonNode functionArgs = functionRef.getArguments();
+        if (functionArgs != null) {
+            nodeFactory.metaData(SWFDecisionEngine.EXPR_LANG, 
workflow.getExpressionLang());
+            MappingUtils.processArgs(workflow, functionArgs, new 
MappingSetter() {
+                @Override
+                public void accept(String key, Object value) {
+                    nodeFactory.parameter(key, value);
+                }
+
+                @Override
+                public void accept(Object value) {
+                    nodeFactory.parameter(SWFConstants.CONTENT_DATA, value);
+                }
+            });
+        }
+        return nodeFactory;
+    }
+
+    private DecisionModel loadDMNFromFile(String namespace, String model, 
String file) {
+        try (Reader reader = new 
InputStreamReader(URIContentLoaderFactory.builder(file).withClassloader(this.getClass().getClassLoader()).build().getInputStream()))
 {
+            return new 
DmnDecisionModel(DMNKogito.createGenericDMNRuntime(reader), namespace, model);
+        } catch (IOException io) {
+            throw new UncheckedIOException(io);
+        }
+    }
+}
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler
 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler
new file mode 100644
index 0000000000..8169c5bf60
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler
@@ -0,0 +1 @@
+org.kie.kogito.serverless.workflow.parser.types.DMNTypeHandler
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/test/java/org/kie/kogito/serverless/workflow/dmn/SWFDMNTest.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/test/java/org/kie/kogito/serverless/workflow/dmn/SWFDMNTest.java
new file mode 100644
index 0000000000..d2828b9893
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn-parser/src/test/java/org/kie/kogito/serverless/workflow/dmn/SWFDMNTest.java
@@ -0,0 +1,57 @@
+package org.kie.kogito.serverless.workflow.dmn;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.kie.kogito.serverless.workflow.executor.StaticWorkflowApplication;
+import org.kie.kogito.serverless.workflow.parser.types.DMNTypeHandler;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+import io.serverlessworkflow.api.Workflow;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.kie.kogito.serverless.workflow.fluent.ActionBuilder.call;
+import static org.kie.kogito.serverless.workflow.fluent.FunctionBuilder.custom;
+import static org.kie.kogito.serverless.workflow.fluent.StateBuilder.operation;
+import static 
org.kie.kogito.serverless.workflow.fluent.WorkflowBuilder.workflow;
+
+public class SWFDMNTest {
+    @Test
+    void testDMNFile() throws IOException {
+        doIt(buildWorkflow(Collections.emptyMap()));
+    }
+
+    @Test
+    void testDMNFileWithArgs() throws IOException {
+        doIt(buildWorkflow(Map.of("Driver", ".Driver", "Violation", 
".Violation")));
+    }
+
+    @Test
+    void testDMNFileWithExprArg() throws IOException {
+        doIt(buildWorkflow("{Driver:.Driver,Violation:.Violation}"));
+    }
+
+    private void doIt(Workflow workflow) {
+        try (StaticWorkflowApplication application = 
StaticWorkflowApplication.create()) {
+            JsonNode response = application.execute(workflow, Map.of("Driver", 
Map.of("Name", "Pepe", "Age", 19, "Points", 0, "State", "Spain", "City", 
"Zaragoza"), "Violation", Map.of("Code", "12",
+                    "Date", new Date(System.currentTimeMillis()), "Type", 
"parking"))).getWorkflowdata();
+            assertThat(response.get("Should the driver be 
suspended?")).isEqualTo(new TextNode("No"));
+            response = application.execute(workflow, Map.of("Driver", 
Map.of("Name", "Pepe", "Age", 19, "Points", 19, "State", "Spain", "City", 
"Zaragoza"), "Violation", Map.of("Code", "12",
+                    "Date", new Date(System.currentTimeMillis()), "Type", 
"speed", "Speed Limit", "120", "Actual Speed", "180"))).getWorkflowdata();
+            assertThat(response.get("Should the driver be 
suspended?")).isEqualTo(new TextNode("Yes"));
+        }
+    }
+
+    private Workflow buildWorkflow(Object args) {
+        return workflow("PlayingWithDMN")
+                .start(operation().action(call(custom("DMNTest", 
"dmn").metadata(DMNTypeHandler.FILE, "classpath:valid_models/DMNv1_x/Traffic 
Violation Simple.dmn")
+                        .metadata(DMNTypeHandler.MODEL, "Traffic Violation")
+                        .metadata(DMNTypeHandler.NAMESPACE, 
"https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF";),
 args)))
+                .end().build();
+    }
+}
diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-dmn/pom.xml 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn/pom.xml
new file mode 100644
index 0000000000..7a8d68802b
--- /dev/null
+++ b/kogito-serverless-workflow/kogito-serverless-workflow-dmn/pom.xml
@@ -0,0 +1,24 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
https://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.kie.kogito</groupId>
+    <artifactId>kogito-serverless-workflow</artifactId>
+    <version>999-SNAPSHOT</version>
+  </parent>
+  <artifactId>kogito-serverless-workflow-dmn</artifactId>
+  <name>Kogito :: Serverless Workflow :: DMN :: Runtime</name>
+  <properties>
+      
<java.module.name>org.kie.kogito.serverless.workflow.dmn.runtime</java.module.name>
+  </properties>
+
+  <dependencies>
+        <dependency>
+      <groupId>org.kie.kogito</groupId>
+      <artifactId>kogito-serverless-workflow-runtime</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.kie.kogito</groupId>
+      <artifactId>kogito-dmn</artifactId>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-dmn/src/main/java/org/kie/kogito/serverless/workflow/dmn/SWFDecisionEngine.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn/src/main/java/org/kie/kogito/serverless/workflow/dmn/SWFDecisionEngine.java
new file mode 100644
index 0000000000..c84acfc652
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn/src/main/java/org/kie/kogito/serverless/workflow/dmn/SWFDecisionEngine.java
@@ -0,0 +1,96 @@
+package org.kie.kogito.serverless.workflow.dmn;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.jbpm.util.ContextFactory;
+import org.jbpm.workflow.core.impl.NodeIoHelper;
+import org.jbpm.workflow.core.node.RuleSetNode;
+import org.jbpm.workflow.instance.node.RuleSetNodeInstance;
+import org.jbpm.workflow.instance.rule.DecisionRuleTypeEngine;
+import org.kie.api.runtime.KieSession;
+import org.kie.dmn.api.core.DMNContext;
+import org.kie.dmn.api.core.DMNMessage;
+import org.kie.dmn.api.core.DMNResult;
+import org.kie.dmn.api.core.DMNRuntime;
+import org.kie.kogito.decision.DecisionModel;
+import org.kie.kogito.dmn.DmnDecisionModel;
+import org.kie.kogito.dmn.rest.DMNJSONUtils;
+import org.kie.kogito.internal.process.runtime.KogitoProcessContext;
+import org.kie.kogito.jackson.utils.JsonObjectUtils;
+import org.kie.kogito.process.expr.Expression;
+import org.kie.kogito.process.expr.ExpressionHandlerFactory;
+import org.kie.kogito.serverless.workflow.SWFConstants;
+
+import static org.kie.kogito.serverless.workflow.SWFConstants.CONTENT_DATA;
+
+public class SWFDecisionEngine implements DecisionRuleTypeEngine {
+
+    public static final String EXPR_LANG = "lang";
+
+    @Override
+    public void evaluate(RuleSetNodeInstance rsni, String inputNamespace, 
String inputModel, String decision) {
+        String namespace = rsni.resolveExpression(inputNamespace);
+        String model = rsni.resolveExpression(inputModel);
+        DecisionModel modelInstance =
+                Optional.ofNullable(rsni.getRuleSetNode().getDecisionModel())
+                        .orElse(() -> new DmnDecisionModel(
+                                ((KieSession) 
getKieRuntime(rsni)).getKieRuntime(DMNRuntime.class),
+                                namespace,
+                                model))
+                        .get();
+
+        //Input Binding
+        DMNContext context = DMNJSONUtils.ctx(modelInstance, 
getInputParameters(rsni));
+        DMNResult dmnResult = modelInstance.evaluateAll(context);
+        if (dmnResult.hasErrors()) {
+            String errors = 
dmnResult.getMessages(DMNMessage.Severity.ERROR).stream()
+                    .map(Object::toString)
+                    .collect(Collectors.joining(", "));
+
+            throw new RuntimeException("DMN result errors:: " + errors);
+        }
+        //Output Binding
+        Map<String, Object> outputSet = Map.of(SWFConstants.RESULT, 
dmnResult.getContext().getAll());
+        NodeIoHelper.processOutputs(rsni, outputSet::get, rsni::getVariable);
+
+        rsni.triggerCompleted();
+    }
+
+    private Map<String, Object> getInputParameters(RuleSetNodeInstance rsni) {
+        RuleSetNode node = rsni.getRuleSetNode();
+        Map<String, Object> inputParameters = node.getParameters();
+        int size = inputParameters.size();
+        if (size == 0) {
+            inputParameters = 
JsonObjectUtils.convertValue(getInputs(rsni).get(SWFConstants.MODEL_WORKFLOW_VAR),
 Map.class);
+        } else if (size == 1 && inputParameters.containsKey(CONTENT_DATA)) {
+            return eval(ContextFactory.fromNode(rsni), 
ExpressionHandlerFactory.get((String) node.getMetaData().get(EXPR_LANG), 
(String) inputParameters.get(CONTENT_DATA)));
+        } else {
+            inputParameters = 
getInputParameters(ContextFactory.fromNode(rsni), (String) 
node.getMetaData().get(EXPR_LANG), new HashMap<>(inputParameters));
+        }
+        return inputParameters;
+
+    }
+
+    private Map<String, Object> getInputParameters(KogitoProcessContext 
context, String exprLang, Map<String, Object> inputParameters) {
+        for (Map.Entry<String, Object> entry : inputParameters.entrySet()) {
+            Object value = entry.getValue();
+            if (value instanceof Map) {
+                entry.setValue(getInputParameters(context, exprLang, 
(Map<String, Object>) value));
+            } else if (value instanceof CharSequence) {
+                Expression expr = ExpressionHandlerFactory.get(exprLang, 
value.toString());
+                if (expr.isValid()) {
+                    entry.setValue(eval(context, expr));
+                }
+            }
+        }
+        return inputParameters;
+    }
+
+    private Map<String, Object> eval(KogitoProcessContext context, Expression 
expr) {
+        return 
expr.eval(JsonObjectUtils.fromValue(context.getVariable(SWFConstants.DEFAULT_WORKFLOW_VAR)),
 Map.class, context);
+    }
+
+}
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-dmn/src/main/resources/META-INF/services/org.jbpm.workflow.instance.rule.DecisionRuleTypeEngine
 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn/src/main/resources/META-INF/services/org.jbpm.workflow.instance.rule.DecisionRuleTypeEngine
new file mode 100644
index 0000000000..077e0fb2c7
--- /dev/null
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-dmn/src/main/resources/META-INF/services/org.jbpm.workflow.instance.rule.DecisionRuleTypeEngine
@@ -0,0 +1 @@
+org.kie.kogito.serverless.workflow.dmn.SWFDecisionEngine
\ No newline at end of file
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/FunctionBuilder.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/FunctionBuilder.java
index cc2182c414..1abe1d2fca 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/FunctionBuilder.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-fluent/src/main/java/org/kie/kogito/serverless/workflow/fluent/FunctionBuilder.java
@@ -76,12 +76,15 @@ public class FunctionBuilder {
     }
 
     private static FunctionBuilder service(String name, String langName, 
String moduleName, String methodName) {
-        return new FunctionBuilder(new 
FunctionDefinition(name).withType(Type.CUSTOM)
-                .withOperation(ServiceTypeHandler.SERVICE_TYPE + 
CUSTOM_TYPE_SEPARATOR + langName + CUSTOM_TYPE_SEPARATOR + moduleName + 
ServiceTypeHandler.INTFC_SEPARATOR + methodName));
+        return custom(name, ServiceTypeHandler.SERVICE_TYPE + 
CUSTOM_TYPE_SEPARATOR + langName + CUSTOM_TYPE_SEPARATOR + moduleName + 
ServiceTypeHandler.INTFC_SEPARATOR + methodName);
     }
 
     public static FunctionBuilder log(String name, WorkflowLogLevel level) {
-        return new FunctionBuilder(new 
FunctionDefinition(name).withType(Type.CUSTOM).withOperation(SYSOUT_TYPE + 
CUSTOM_TYPE_SEPARATOR + level));
+        return custom(name, SYSOUT_TYPE + CUSTOM_TYPE_SEPARATOR + level);
+    }
+
+    public static FunctionBuilder custom(String name, String operation) {
+        return new FunctionBuilder(new 
FunctionDefinition(name).withType(Type.CUSTOM).withOperation(operation));
     }
 
     private FunctionBuilder(FunctionDefinition functionDefinition) {
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/SWFConstants.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/SWFConstants.java
index 20a7b7d5ad..3d822ab0be 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/SWFConstants.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/SWFConstants.java
@@ -21,6 +21,7 @@ package org.kie.kogito.serverless.workflow;
 public class SWFConstants {
 
     public static final String DEFAULT_WORKFLOW_VAR = "workflowdata";
+    public static final String RESULT = "Result";
     public static final String MODEL_WORKFLOW_VAR = "Parameter";
     public static final String CONTENT_DATA = "ContentData";
     public static final String SCRIPT = "script";
diff --git a/kogito-serverless-workflow/pom.xml 
b/kogito-serverless-workflow/pom.xml
index f752d16d20..cfca4a918e 100644
--- a/kogito-serverless-workflow/pom.xml
+++ b/kogito-serverless-workflow/pom.xml
@@ -55,6 +55,8 @@
     <module>kogito-jq-expression</module>
     <module>kogito-serverless-workflow-executor</module>
     <module>kogito-serverless-workflow-executor-tests</module>
+    <module>kogito-serverless-workflow-dmn-parser</module>
+    <module>kogito-serverless-workflow-dmn</module>
   </modules>
 
   <profiles>


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


Reply via email to