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 741ed2fd79 [KOGITO-9811] Evaluating timeout duration as expression 
(#3250)
741ed2fd79 is described below

commit 741ed2fd79898b64d7093444bd5cb126bc883987
Author: Francisco Javier Tirado Sarti 
<[email protected]>
AuthorDate: Tue Nov 7 12:03:45 2023 +0100

    [KOGITO-9811] Evaluating timeout duration as expression (#3250)
    
    * [KOGITO-9811] Moving expression from ForEach to Process
    
    * [KOGITO-9811] Resolving timer on runtime
    
    * [KOGITO-9811] Improving message to include the expression possibility
---
 .../compiler/canonical/ForEachNodeVisitor.java     |  4 ---
 .../jbpm/compiler/canonical/ProcessVisitor.java    |  7 ++++
 .../jbpm/ruleflow/core/RuleFlowProcessFactory.java |  5 +++
 .../ruleflow/core/factory/ForEachNodeFactory.java  |  5 ---
 .../core/validation/RuleFlowProcessValidator.java  | 36 ++++++++++++-------
 .../org/jbpm/workflow/core/WorkflowProcess.java    |  3 ++
 .../java/org/jbpm/workflow/core/impl/NodeImpl.java | 40 +++++++++++++++++++++
 .../workflow/core/impl/WorkflowProcessImpl.java    |  9 +++++
 .../org/jbpm/workflow/core/node/ForEachNode.java   | 11 ++----
 .../instance/node/StateBasedNodeInstance.java      | 42 +++++++++++++++++-----
 .../workflow/parser/ServerlessWorkflowParser.java  |  2 ++
 .../parser/handlers/ForEachStateHandler.java       |  2 +-
 .../workflow/utils/TimeoutsConfigResolver.java     | 25 ++++++++-----
 .../workflow/utils/TimeoutsConfigResolverTest.java |  1 +
 ...lback-state-with-timeouts-error-handler.sw.json |  3 +-
 15 files changed, 146 insertions(+), 49 deletions(-)

diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ForEachNodeVisitor.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ForEachNodeVisitor.java
index 7fe518fcba..422c2dac03 100644
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ForEachNodeVisitor.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ForEachNodeVisitor.java
@@ -96,10 +96,6 @@ public class ForEachNodeVisitor extends 
AbstractCompositeNodeVisitor<ForEachNode
                     
buildDataResolver(node.getOutputVariableType().getStringType())));
         }
 
-        if (node.getExpressionLanguage() != null) {
-            body.addStatement(getFactoryMethod(getNodeId(node), 
"expressionLanguage", new StringLiteralExpr(node.getExpressionLanguage())));
-        }
-
         if (node.getCompletionAction() instanceof ExpressionSupplier) {
             body.addStatement(getFactoryMethod(getNodeId(node), 
"completionAction", ((ExpressionSupplier) node.getCompletionAction()).get(node, 
metadata)));
         }
diff --git 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessVisitor.java
 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessVisitor.java
index d82cca9ded..ec9b667b91 100644
--- 
a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessVisitor.java
+++ 
b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessVisitor.java
@@ -149,6 +149,13 @@ public class ProcessVisitor extends AbstractVisitor {
         visitVariableScope(FACTORY_FIELD_NAME, variableScope, body, 
visitedVariables, metadata.getProcessClassName());
         visitSubVariableScopes(process.getNodes(), body, visitedVariables);
 
+        if (process instanceof org.jbpm.workflow.core.WorkflowProcess) {
+            org.jbpm.workflow.core.WorkflowProcess processImpl = 
(org.jbpm.workflow.core.WorkflowProcess) process;
+            if (processImpl.getExpressionLanguage() != null) {
+                body.addStatement(getFactoryMethod(FACTORY_FIELD_NAME, 
"expressionLanguage", new 
StringLiteralExpr(processImpl.getExpressionLanguage())));
+            }
+        }
+
         visitInterfaces(process.getNodes());
 
         metadata.setDynamic(((org.jbpm.workflow.core.WorkflowProcess) 
process).isDynamic());
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/RuleFlowProcessFactory.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/RuleFlowProcessFactory.java
index 47c3ea5908..c9e82a7dfe 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/RuleFlowProcessFactory.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/RuleFlowProcessFactory.java
@@ -105,6 +105,11 @@ public class RuleFlowProcessFactory extends 
RuleFlowNodeContainerFactory<RuleFlo
         getRuleFlowProcess().setId((String) id);
     }
 
+    public RuleFlowProcessFactory expressionLanguage(String exprLanguage) {
+        getRuleFlowProcess().setExpressionLanguage(exprLanguage);
+        return this;
+    }
+
     protected RuleFlowProcess getRuleFlowProcess() {
         return (RuleFlowProcess) node;
     }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ForEachNodeFactory.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ForEachNodeFactory.java
index 3ec066530e..aa462310ae 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ForEachNodeFactory.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/factory/ForEachNodeFactory.java
@@ -73,11 +73,6 @@ public class ForEachNodeFactory<T extends 
RuleFlowNodeContainerFactory<T, ?>> ex
         return this;
     }
 
-    public ForEachNodeFactory<T> expressionLanguage(String exprLanguage) {
-        getForEachNode().setExpressionLanguage(exprLanguage);
-        return this;
-    }
-
     public ForEachNodeFactory<T> completionAction(Action completionAction) {
         getForEachNode().setCompletionAction(completionAction);
         return this;
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
index fa7c93b3c8..831354914d 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/ruleflow/core/validation/RuleFlowProcessValidator.java
@@ -81,6 +81,7 @@ import org.kie.api.definition.process.NodeContainer;
 import org.kie.api.definition.process.Process;
 import org.kie.api.io.Resource;
 import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess;
+import org.kie.kogito.process.expr.ExpressionHandlerFactory;
 
 import static java.lang.String.format;
 import static org.jbpm.ruleflow.core.Metadata.EVENT_TYPE;
@@ -855,6 +856,11 @@ public class RuleFlowProcessValidator implements 
ProcessValidator {
                 (nodeContainer instanceof WorkflowProcess && 
((WorkflowProcess) nodeContainer).isDynamic());
     }
 
+    private boolean isExpression(RuleFlowProcess process, String expression) {
+        String lang = process.getExpressionLanguage();
+        return lang != null && ExpressionHandlerFactory.get(lang, 
expression).isValid();
+    }
+
     private void validateTimer(final Timer timer,
             final org.kie.api.definition.process.Node node,
             final RuleFlowProcess process,
@@ -884,10 +890,12 @@ public class RuleFlowProcessValidator implements 
ProcessValidator {
                             break;
                     }
                 } catch (RuntimeException e) {
-                    addErrorMessage(process,
-                            node,
-                            errors,
-                            "Could not parse delay '" + timer.getDelay() + "': 
" + e.getMessage());
+                    if (!isExpression(process, timer.getDelay())) {
+                        addErrorMessage(process,
+                                node,
+                                errors,
+                                "Could not parse delay '" + timer.getDelay() + 
"': " + e.getMessage());
+                    }
                 }
             }
         }
@@ -898,10 +906,12 @@ public class RuleFlowProcessValidator implements 
ProcessValidator {
                     DateTimeUtils.parseRepeatableDateTime(timer.getPeriod());
                 }
             } catch (RuntimeException e) {
-                addErrorMessage(process,
-                        node,
-                        errors,
-                        "Could not parse period '" + timer.getPeriod() + "': " 
+ e.getMessage());
+                if (!isExpression(process, timer.getPeriod())) {
+                    addErrorMessage(process,
+                            node,
+                            errors,
+                            "Could not parse period '" + timer.getPeriod() + 
"': " + e.getMessage());
+                }
             }
         }
 
@@ -909,10 +919,12 @@ public class RuleFlowProcessValidator implements 
ProcessValidator {
             try {
                 DateTimeUtils.parseDateAsDuration(timer.getDate());
             } catch (RuntimeException e) {
-                addErrorMessage(process,
-                        node,
-                        errors,
-                        "Could not parse date '" + timer.getDate() + "': " + 
e.getMessage());
+                if (!isExpression(process, timer.getDate())) {
+                    addErrorMessage(process,
+                            node,
+                            errors,
+                            "Could not parse date '" + timer.getDate() + "': " 
+ e.getMessage());
+                }
             }
         }
     }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/WorkflowProcess.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/WorkflowProcess.java
index fd04d3ebd5..b25e001e91 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/WorkflowProcess.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/WorkflowProcess.java
@@ -58,4 +58,7 @@ public interface WorkflowProcess extends 
KogitoWorkflowProcess, Process, NodeCon
 
     void setOutputValidator(WorkflowModelValidator validator);
 
+    void setExpressionLanguage(String exprLanguage);
+
+    String getExpressionLanguage();
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java
index e364abaafc..e58e08bb24 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/NodeImpl.java
@@ -28,8 +28,10 @@ import java.util.concurrent.atomic.AtomicLong;
 import org.jbpm.process.core.Context;
 import org.jbpm.process.core.ContextResolver;
 import org.jbpm.process.core.context.variable.Mappable;
+import org.jbpm.ruleflow.core.RuleFlowProcess;
 import org.jbpm.workflow.core.Constraint;
 import org.jbpm.workflow.core.Node;
+import org.jbpm.workflow.core.WorkflowProcess;
 import org.jbpm.workflow.core.node.CompositeNode;
 import org.kie.api.definition.process.Connection;
 import org.kie.api.definition.process.NodeContainer;
@@ -80,52 +82,72 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return ioSpecification;
     }
 
+    @Override
     public Map<String, String> getInMappings() {
         return getIoSpecification().getInputMapping();
     }
 
+    @Override
     public Map<String, String> getOutMappings() {
         return getIoSpecification().getOutputMappingBySources();
     }
 
+    @Override
     public String getInMapping(String key) {
         return getIoSpecification().getInputMapping().get(key);
     }
 
+    @Override
     public String getOutMapping(String key) {
         return getIoSpecification().getOutputMappingBySources().get(key);
     }
 
+    @Override
     public void addInMapping(String from, String to) {
         getIoSpecification().addInputMapping(from, to);
     }
 
+    @Override
     public void addOutMapping(String from, String to) {
         getIoSpecification().addOutputMapping(from, to);
     }
 
+    @Override
     public void addInAssociation(DataAssociation dataAssociation) {
         getIoSpecification().getDataInputs().add(dataAssociation.getTarget());
         getIoSpecification().getDataInputAssociation().add(dataAssociation);
     }
 
+    @Override
     public List<DataAssociation> getInAssociations() {
         return getIoSpecification().getDataInputAssociation();
     }
 
+    @Override
     public void addOutAssociation(DataAssociation dataAssociation) {
         dataAssociation.getSources().forEach(s -> 
getIoSpecification().getDataOutputs().add(s));
         getIoSpecification().getDataOutputAssociation().add(dataAssociation);
     }
 
+    public WorkflowProcess getProcess() {
+        NodeContainer container = parentContainer;
+        while (!(container instanceof RuleFlowProcess)) {
+            container = ((NodeImpl) container).parentContainer;
+        }
+        return (WorkflowProcess) container;
+    }
+
+    @Override
     public List<DataAssociation> getOutAssociations() {
         return getIoSpecification().getDataOutputAssociation();
     }
 
+    @Override
     public long getId() {
         return this.id;
     }
 
+    @Override
     public String getUniqueId() {
         StringBuilder result = new StringBuilder(id + "");
         NodeContainer nodeContainer = getParentContainer();
@@ -137,6 +159,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return result.toString();
     }
 
+    @Override
     public void setId(final long id) {
         this.id = id;
         String uniqueId = (String) getMetaData("UniqueId");
@@ -145,24 +168,29 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         }
     }
 
+    @Override
     public String getName() {
         return this.name;
     }
 
+    @Override
     public void setName(final String name) {
         this.name = name;
     }
 
+    @Override
     public Map<String, List<Connection>> getIncomingConnections() {
         // TODO: users can still modify the lists inside this Map
         return Collections.unmodifiableMap(this.incomingConnections);
     }
 
+    @Override
     public Map<String, List<Connection>> getOutgoingConnections() {
         // TODO: users can still modify the lists inside this Map
         return Collections.unmodifiableMap(this.outgoingConnections);
     }
 
+    @Override
     public void addIncomingConnection(final String type, final Connection 
connection) {
         validateAddIncomingConnection(type, connection);
         List<Connection> connections = this.incomingConnections.get(type);
@@ -182,6 +210,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         }
     }
 
+    @Override
     public List<Connection> getIncomingConnections(String type) {
         List<Connection> result = incomingConnections.get(type);
         if (result == null) {
@@ -190,6 +219,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return result;
     }
 
+    @Override
     public void addOutgoingConnection(final String type, final Connection 
connection) {
         validateAddOutgoingConnection(type, connection);
         List<Connection> connections = this.outgoingConnections.get(type);
@@ -209,6 +239,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         }
     }
 
+    @Override
     public List<Connection> getOutgoingConnections(String type) {
         List<Connection> result = outgoingConnections.get(type);
         if (result == null) {
@@ -217,6 +248,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return result;
     }
 
+    @Override
     public void removeIncomingConnection(final String type, final Connection 
connection) {
         validateRemoveIncomingConnection(type, connection);
         this.incomingConnections.get(type).remove(connection);
@@ -243,6 +275,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         }
     }
 
+    @Override
     public void removeOutgoingConnection(final String type, final Connection 
connection) {
         validateRemoveOutgoingConnection(type, connection);
         this.outgoingConnections.get(type).remove(connection);
@@ -315,22 +348,27 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return getOutgoingConnections(Node.CONNECTION_DEFAULT_TYPE);
     }
 
+    @Override
     public NodeContainer getParentContainer() {
         return parentContainer;
     }
 
+    @Override
     public void setParentContainer(NodeContainer nodeContainer) {
         this.parentContainer = nodeContainer;
     }
 
+    @Override
     public void setContext(String contextId, Context context) {
         this.contexts.put(contextId, context);
     }
 
+    @Override
     public Context getContext(String contextId) {
         return this.contexts.get(contextId);
     }
 
+    @Override
     public Context resolveContext(String contextId, Object param) {
         Context context = getContext(contextId);
         if (context != null) {
@@ -342,6 +380,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return ((org.jbpm.workflow.core.NodeContainer) 
parentContainer).resolveContext(contextId, param);
     }
 
+    @Override
     public void setMetaData(String name, Object value) {
         this.metaData.put(name, value);
     }
@@ -350,6 +389,7 @@ public abstract class NodeImpl implements Node, 
ContextResolver, Mappable {
         return this.metaData.get(name);
     }
 
+    @Override
     public Map<String, Object> getMetaData() {
         return this.metaData;
     }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/WorkflowProcessImpl.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/WorkflowProcessImpl.java
index b8ed36b4e8..667a34e1ff 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/WorkflowProcessImpl.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/impl/WorkflowProcessImpl.java
@@ -55,6 +55,7 @@ public class WorkflowProcessImpl extends ProcessImpl 
implements WorkflowProcess,
     private WorkflowModelValidator inputValidator;
     private WorkflowModelValidator outputValidator;
     private org.jbpm.workflow.core.NodeContainer nodeContainer;
+    private String exprLanguage;
 
     private transient BiFunction<String, ProcessInstance, String> 
expressionEvaluator = (expression, p) -> {
 
@@ -239,4 +240,12 @@ public class WorkflowProcessImpl extends ProcessImpl 
implements WorkflowProcess,
     public void setOutputValidator(WorkflowModelValidator outputValidator) {
         this.outputValidator = outputValidator;
     }
+
+    public void setExpressionLanguage(String exprLanguage) {
+        this.exprLanguage = exprLanguage;
+    }
+
+    public String getExpressionLanguage() {
+        return exprLanguage;
+    }
 }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/ForEachNode.java 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/ForEachNode.java
index a13fd0557b..8b7ea883ad 100755
--- a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/ForEachNode.java
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/ForEachNode.java
@@ -50,7 +50,7 @@ public class ForEachNode extends CompositeContextNode {
     private String collectionExpression;
     private String outputCollectionExpression;
     private String completionConditionExpression;
-    private String exprLanguage;
+
     private Action finishAction;
     private boolean waitForCompletion = true;
     private Expression evaluateExpression;
@@ -108,14 +108,6 @@ public class ForEachNode extends CompositeContextNode {
         return null;
     }
 
-    public void setExpressionLanguage(String exprLanguage) {
-        this.exprLanguage = exprLanguage;
-    }
-
-    public String getExpressionLanguage() {
-        return exprLanguage;
-    }
-
     public Action getCompletionAction() {
         return finishAction;
     }
@@ -125,6 +117,7 @@ public class ForEachNode extends CompositeContextNode {
     }
 
     public Expression getEvaluateExpression() {
+        String exprLanguage = getProcess().getExpressionLanguage();
         if (evaluateExpression == null && 
ExpressionHandlerFactory.isSupported(exprLanguage)) {
             evaluateExpression = ExpressionHandlerFactory.get(exprLanguage, 
collectionExpression);
         }
diff --git 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
index 0026482fe7..68d8150838 100755
--- 
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
+++ 
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
@@ -34,8 +34,11 @@ import org.jbpm.process.core.timer.DateTimeUtils;
 import org.jbpm.process.core.timer.Timer;
 import org.jbpm.process.instance.InternalProcessRuntime;
 import org.jbpm.process.instance.impl.Action;
+import org.jbpm.ruleflow.core.Metadata;
 import org.jbpm.util.ContextFactory;
 import org.jbpm.workflow.core.DroolsAction;
+import org.jbpm.workflow.core.WorkflowProcess;
+import org.jbpm.workflow.core.impl.NodeImpl;
 import org.jbpm.workflow.core.node.StateBasedNode;
 import org.jbpm.workflow.instance.impl.ExtendedNodeInstanceImpl;
 import org.jbpm.workflow.instance.impl.WorkflowProcessInstanceImpl;
@@ -51,6 +54,8 @@ import org.kie.kogito.jobs.ExactExpirationTime;
 import org.kie.kogito.jobs.ExpirationTime;
 import org.kie.kogito.jobs.JobsService;
 import org.kie.kogito.jobs.ProcessInstanceJobDescription;
+import org.kie.kogito.process.expr.Expression;
+import org.kie.kogito.process.expr.ExpressionHandlerFactory;
 import org.kie.kogito.timer.TimerInstance;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -68,6 +73,8 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
 
     private Map<String, String> timerInstancesReference;
 
+    private transient KogitoProcessContext context;
+
     public StateBasedNode getEventBasedNode() {
         return (StateBasedNode) getNode();
     }
@@ -144,8 +151,8 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
             switch (timer.getTimeType()) {
                 case Timer.TIME_CYCLE:
 
-                    String tempDelay = resolveExpression(timer.getDelay());
-                    String tempPeriod = resolveExpression(timer.getPeriod());
+                    String tempDelay = 
resolveTimerExpression(timer.getDelay());
+                    String tempPeriod = 
resolveTimerExpression(timer.getPeriod());
                     if (DateTimeUtils.isRepeatable(tempDelay)) {
                         String[] values = 
DateTimeUtils.parseISORepeatable(tempDelay);
                         String tempRepeatLimit = values[0];
@@ -174,7 +181,7 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
                     }
 
                 case Timer.TIME_DURATION:
-                    delay = resolveExpression(timer.getDelay());
+                    delay = resolveTimerExpression(timer.getDelay());
 
                     return 
DurationExpirationTime.repeat(businessCalendar.calculateBusinessTimeAsDuration(delay));
                 case Timer.TIME_DATE:
@@ -196,14 +203,14 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
             case Timer.TIME_CYCLE:
                 if (timer.getPeriod() != null) {
 
-                    long actualDelay = 
DateTimeUtils.parseDuration(resolveExpression(timer.getDelay()));
+                    long actualDelay = 
DateTimeUtils.parseDuration(resolveTimerExpression(timer.getDelay()));
                     if (timer.getPeriod() == null) {
                         return DurationExpirationTime.repeat(actualDelay, 
actualDelay, Integer.MAX_VALUE);
                     } else {
-                        return DurationExpirationTime.repeat(actualDelay, 
DateTimeUtils.parseDuration(resolveExpression(timer.getPeriod())), 
Integer.MAX_VALUE);
+                        return DurationExpirationTime.repeat(actualDelay, 
DateTimeUtils.parseDuration(resolveTimerExpression(timer.getPeriod())), 
Integer.MAX_VALUE);
                     }
                 } else {
-                    String resolvedDelay = resolveExpression(timer.getDelay());
+                    String resolvedDelay = 
resolveTimerExpression(timer.getDelay());
 
                     // when using ISO date/time period is not set
                     long[] repeatValues = null;
@@ -233,7 +240,7 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
                     duration = DateTimeUtils.parseDuration(timer.getDelay());
                 } catch (RuntimeException e) {
                     // cannot parse delay, trying to interpret it
-                    s = resolveExpression(timer.getDelay());
+                    s = resolveTimerExpression(timer.getDelay());
                     duration = DateTimeUtils.parseDuration(s);
                 }
                 return DurationExpirationTime.after(duration);
@@ -243,13 +250,32 @@ public abstract class StateBasedNodeInstance extends 
ExtendedNodeInstanceImpl im
                     return ExactExpirationTime.of(timer.getDate());
                 } catch (RuntimeException e) {
                     // cannot parse delay, trying to interpret it
-                    s = resolveExpression(timer.getDate());
+                    s = resolveTimerExpression(timer.getDate());
                     return ExactExpirationTime.of(s);
                 }
         }
         throw new UnsupportedOperationException("Not supported timer 
definition");
     }
 
+    private String resolveTimerExpression(String expression) {
+        if (!isExpression(expression)) {
+            WorkflowProcess process = ((NodeImpl) getNode()).getProcess();
+            String lang = process.getExpressionLanguage();
+            if (lang != null) {
+                Expression exprObject = ExpressionHandlerFactory.get(lang, 
expression);
+                if (exprObject.isValid()) {
+                    if (context == null) {
+                        context = ContextFactory.fromNode(this);
+                    }
+                    String varName = (String) 
process.getMetaData().get(Metadata.VARIABLE);
+                    Object target = varName == null ? 
this.getProcessInstance().getVariables() : context.getVariable(varName);
+                    return exprObject.eval(target, String.class, context);
+                }
+            }
+        }
+        return resolveExpression(expression);
+    }
+
     protected void handleSLAViolation() {
         if (slaCompliance == KogitoProcessInstance.SLA_PENDING) {
             InternalProcessRuntime processRuntime = ((InternalProcessRuntime) 
getProcessInstance().getKnowledgeRuntime().getProcessRuntime());
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java
index 0432d83e5d..edc9f52b09 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/ServerlessWorkflowParser.java
@@ -128,6 +128,8 @@ public class ServerlessWorkflowParser {
                 .packageName(workflow.getMetadata() != null ? 
workflow.getMetadata().getOrDefault("package",
                         DEFAULT_PACKAGE) : DEFAULT_PACKAGE)
                 .visibility("Public")
+                .expressionLanguage(workflow.getExpressionLang())
+                .metaData(Metadata.VARIABLE, DEFAULT_WORKFLOW_VAR)
                 .variable(DEFAULT_WORKFLOW_VAR, new 
ObjectDataType(JsonNode.class), 
ObjectMapperFactory.listenerAware().createObjectNode())
                 .type(KogitoWorkflowProcess.SW_TYPE);
         ParserContext parserContext =
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/ForEachStateHandler.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/ForEachStateHandler.java
index e06d5fac34..b9fab37eea 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/ForEachStateHandler.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/parser/handlers/ForEachStateHandler.java
@@ -43,7 +43,7 @@ public class ForEachStateHandler extends 
CompositeContextNodeHandler<ForEachStat
     @Override
     protected MakeNodeResult makeNode(RuleFlowNodeContainerFactory<?, ?> 
factory) {
         ForEachNodeFactory<?> result =
-                
factory.forEachNode(parserContext.newId()).sequential(false).waitForCompletion(true).expressionLanguage(workflow.getExpressionLang()).collectionExpression(state.getInputCollection())
+                
factory.forEachNode(parserContext.newId()).sequential(false).waitForCompletion(true).collectionExpression(state.getInputCollection())
                         .outputVariable(FOR_EACH_OUTPUT_VARIABLE, new 
ObjectDataType(JsonNode.class))
                         .metaData(Metadata.VARIABLE, DEFAULT_WORKFLOW_VAR)
                         .tempVariable(TEMP_OUTPUT_VAR, new 
ObjectDataType(JsonNode.class));
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolver.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolver.java
index 10ace1d10e..7a8bcb52a2 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolver.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/main/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolver.java
@@ -21,15 +21,18 @@ package org.kie.kogito.serverless.workflow.utils;
 import java.time.Duration;
 import java.time.format.DateTimeParseException;
 
+import org.kie.kogito.process.expr.ExpressionHandlerFactory;
+
 import io.serverlessworkflow.api.Workflow;
 import io.serverlessworkflow.api.interfaces.State;
 import io.serverlessworkflow.api.timeouts.TimeoutsDefinition;
 
 public class TimeoutsConfigResolver {
 
-    private static final String NON_NEGATIVE_DURATION_MUST_BE_PROVIDED = "When 
configured, it must be set with a greater than zero ISO 8601 time duration. For 
example PT30S.";
+    private static final String NON_NEGATIVE_DURATION_MUST_BE_PROVIDED =
+            "When configured, it must be set with a greater than zero ISO 8601 
time duration. For example PT30S. Or a valid expression, for example 
$CONST.myDuration, where 'myDuration' is defined in the constant section of the 
workflow";
 
-    private static final String INVALID_EVENT_TIMEOUT_FOR_STATE_ERROR = "An 
invalid \"eventTimeout\": \"%s\" configuration was provided for the state 
\"%s\" in the serverless workflow: \"%s\". " +
+    private static final String INVALID_EVENT_TIMEOUT_FOR_STATE_ERROR = "An 
invalid \"eventTimeout\": \"%s\" configuration was provided for the state 
\"%s\" in the serverless workflow: \"%s\"." +
             NON_NEGATIVE_DURATION_MUST_BE_PROVIDED;
 
     private static final String INVALID_EVENT_TIMEOUT_FOR_WORKFLOW_ERROR = "An 
invalid \"eventTimeout\": \"%s\" configuration was provided for the serverless 
workflow: \"%s\". " +
@@ -45,7 +48,8 @@ public class TimeoutsConfigResolver {
                     String.format(INVALID_EVENT_TIMEOUT_FOR_STATE_ERROR,
                             timeouts.getEventTimeout(),
                             state.getName(),
-                            workflow.getName()));
+                            workflow.getName()),
+                    workflow.getExpressionLang());
             return timeouts.getEventTimeout();
         } else {
             timeouts = workflow.getTimeouts();
@@ -53,18 +57,21 @@ public class TimeoutsConfigResolver {
                 validateDuration(timeouts.getEventTimeout(),
                         String.format(INVALID_EVENT_TIMEOUT_FOR_WORKFLOW_ERROR,
                                 timeouts.getEventTimeout(),
-                                workflow.getName()));
+                                workflow.getName()),
+                        workflow.getExpressionLang());
                 return timeouts.getEventTimeout();
             }
         }
         return null;
     }
 
-    private static void validateDuration(String value, String message) {
-        try {
-            Duration.parse(value);
-        } catch (DateTimeParseException e) {
-            throw new IllegalArgumentException(message, e);
+    private static void validateDuration(String value, String message, String 
exprLanguage) {
+        if (!ExpressionHandlerFactory.get(exprLanguage, value).isValid()) {
+            try {
+                Duration.parse(value);
+            } catch (DateTimeParseException e) {
+                throw new IllegalArgumentException(message, e);
+            }
         }
     }
 }
\ No newline at end of file
diff --git 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolverTest.java
 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolverTest.java
index 0e638572b7..deb3dcb989 100644
--- 
a/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolverTest.java
+++ 
b/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/java/org/kie/kogito/serverless/workflow/utils/TimeoutsConfigResolverTest.java
@@ -97,6 +97,7 @@ class TimeoutsConfigResolverTest {
 
     private static Workflow mockWorkflow(String name) {
         Workflow workflow = mock(Workflow.class);
+        doReturn("jq").when(workflow).getExpressionLang();
         doReturn(name).when(workflow).getName();
         return workflow;
     }
diff --git 
a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json
 
b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json
index d522faa1f2..eb5508c0ff 100644
--- 
a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json
+++ 
b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json
@@ -5,6 +5,7 @@
   "expressionLang": "jsonpath",
   "description": "Callback State With Timeouts Error Handler Test",
   "start": "CallbackState",
+  "constants": {"duration":"PT5S"},
   "events": [
     {
       "name": "callbackEvent",
@@ -70,7 +71,7 @@
         }
       ],
       "timeouts": {
-        "eventTimeout": "PT5S"
+        "eventTimeout": "$CONST.duration"
       }
     },
     {


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


Reply via email to