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]