Skip to content

Commit

Permalink
[KOGITO-9811] Resolving timer on runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
fjtirado committed Nov 2, 2023
1 parent 5daf34f commit f36e0ff
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
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;
Expand Down Expand Up @@ -855,6 +856,11 @@ private boolean acceptsNoOutgoingConnections(org.kie.api.definition.process.Node
(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,
Expand Down Expand Up @@ -884,10 +890,12 @@ private void validateTimer(final Timer timer,
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());
}
}
}
}
Expand All @@ -898,21 +906,25 @@ private void validateTimer(final Timer timer,
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());
}
}
}

if (timer.getDate() != null && !timer.getDate().contains("#{")) {
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());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
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;
Expand All @@ -51,6 +54,8 @@
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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -144,8 +151,8 @@ protected ExpirationTime createTimerInstance(Timer timer) {
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];
Expand Down Expand Up @@ -174,7 +181,7 @@ protected ExpirationTime createTimerInstance(Timer timer) {
}

case Timer.TIME_DURATION:
delay = resolveExpression(timer.getDelay());
delay = resolveTimerExpression(timer.getDelay());

return DurationExpirationTime.repeat(businessCalendar.calculateBusinessTimeAsDuration(delay));
case Timer.TIME_DATE:
Expand All @@ -196,14 +203,14 @@ protected ExpirationTime configureTimerInstance(Timer timer) {
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;
Expand Down Expand Up @@ -233,7 +240,7 @@ protected ExpirationTime configureTimerInstance(Timer timer) {
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);
Expand All @@ -243,13 +250,32 @@ protected ExpirationTime configureTimerInstance(Timer timer) {
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ private GeneratedInfo<KogitoWorkflowProcess> parseProcess() {
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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
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;
Expand All @@ -45,26 +47,30 @@ public static String resolveEventTimeout(State state, Workflow workflow) {
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();
if (timeouts != null && timeouts.getEventTimeout() != null) {
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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private static State mockState(String name, String eventTimeout) {

private static Workflow mockWorkflow(String name) {
Workflow workflow = mock(Workflow.class);
doReturn("jq").when(workflow).getExpressionLang();
doReturn(name).when(workflow).getName();
return workflow;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"expressionLang": "jsonpath",
"description": "Callback State With Timeouts Error Handler Test",
"start": "CallbackState",
"constants": {"duration":"PT5S"},
"events": [
{
"name": "callbackEvent",
Expand Down Expand Up @@ -70,7 +71,7 @@
}
],
"timeouts": {
"eventTimeout": "PT5S"
"eventTimeout": "$CONST.duration"
}
},
{
Expand Down

0 comments on commit f36e0ff

Please sign in to comment.