Scheduled Node
Overview
The Scheduled node is a special type of Action node that executes periodically at regular intervals defined by a cron-like scheduler. Unlike regular Action nodes that execute once when the workflow reaches them, Scheduled nodes run repeatedly until certain conditions are met or until the workflow completes.
A Scheduled node is created by using an Action node with a name that begins with the reserved prefix scheduled_. When the workflow engine detects this prefix, it automatically treats the action as a scheduled task that will be executed periodically by the system's cron scheduler.
Node Properties
| Property | Type | Description |
|---|---|---|
| Name | Text |
The name must begin with the prefix |
| Id | Number |
Unique identifier assigned to the node. This value is automatically generated by the system and uniquely identifies the node within the process definition. |
| Description | Textarea |
Optional field for providing additional details or documentation about the scheduled action's purpose and the logic it executes. |
|
Source position |
Dropdown |
Not applicable. Scheduled nodes do not have source or target positions as they are not connected to other nodes in the workflow diagram. They execute periodically based on the configured schedule. |
|
Target position |
Dropdown |
Not applicable. Scheduled nodes do not have source or target positions. |
|
Script definition |
Script Editor |
Contains the script code that will be executed periodically by the scheduler. The script has full access to the workflow context, variables, and the OpenKM API. The editor can be accessed in two ways:
The editor supports autocomplete functionality. Press |
Important: The node name must begin with scheduled_ for the workflow engine to recognize it as a scheduled node. Without this prefix, it will be treated as a regular Action node.
Execution Schedule Configuration
Scheduled nodes execute periodically based on a cron task whose interval is configurable in the workflow.properties configuration file using the parameter:
scheduled.actions.rate=PT60S
This is the default value, which means scheduled nodes execute every 60 seconds. The value uses ISO 8601 duration format (e.g., PT30S for 30 seconds, PT2M for 2 minutes).
Configuration Changes: After modifying the workflow.properties file, you must restart the service for the changes to take effect.
Common Use Case: Parent-Child Workflow Synchronization
Scheduled nodes are particularly useful when a parent workflow needs to execute multiple child workflows in parallel and wait for all of them to complete before continuing. This pattern is commonly used for parallel approval processes, distributed tasks, or multi-stage operations.
The typical workflow for this pattern involves:
- A parent workflow starts and reaches an Action node that spawns multiple child workflows in parallel
- The parent workflow then waits at a task (typically unassigned) while the child workflows execute
- Each child workflow, upon completion, notifies the parent by setting a variable in the parent's context
- A Scheduled node in the parent workflow periodically checks if all child workflows have completed
- When all child workflows are finished, the Scheduled node completes the waiting task programmatically
- The parent workflow continues to the next step
Implementation Examples
Example 1: Parent Workflow - Spawn Child Workflows
This Action node in the parent workflow creates and starts multiple child workflows in parallel:
import com.openkm.sdk4j.impl.OKMWebservices;
import com.openkm.sdk4j.bean.*;
import com.openkm.okmflow.util.*;
import com.openkm.okmflow.bean.*;
import com.openkm.util.*;
// Load constants
Class Constants = ScriptUtils.evaluateFromNode("library_request_contants");
//Load libraries
Class baseLibrary = ScriptUtils.evaluateFromNode("library_global_base");
// Get the workflow process definition id
def processDefinition = WorkflowUtils.getProcessDefinitionByName("Manager-voting");
long managerPdId = processDefinition.getId();
// Load data from the context
def uuid = baseLibrary.getContextValue(context, Constants.CONTEXT_UUID);
def initiator = baseLibrary.getInitiatorId(context);
def managerSelect = baseLibrary.getContextValue(context, Constants.CONTEXT_MANAGERS);
def managerList = managerSelect.value.tokenize(";");
long processInstanceId = baseLibrary.getProcessInstanceId(context);
// Keep user list in the context
baseLibrary.setContextValue(context, Constants.CONTEXT_MANAGERS_LIST, managerList);
// Run parallel workflows
def actorNum = 0;
managerList.each { manager ->
actorNum++;
def props = [uuid: uuid, initiator: initiator, actor: manager, srcProcInsId: processInstanceId];
def result = WorkflowUtils.runProcessDefinition(managerPdId, props);
context.put("pi_" + manager, result.get());
}
// Keep numbers of manager in the context
baseLibrary.setContextValue(context, Constants.CONTEXT_ACTOR_NUM, actorNum);
Example 2: Child Workflow - Notify Parent on Completion
This Action node in each child workflow notifies the parent when it completes:
import com.openkm.sdk4j.impl.OKMWebservices;
import com.openkm.sdk4j.bean.*;
import com.openkm.okmflow.util.*;
import com.openkm.okmflow.bean.*;
import com.openkm.util.*;
// Load constants
Class Constants = ScriptUtils.evaluateFromNode("library_request_contants");
// Load library
Class baseLibrary = ScriptUtils.evaluateFromNode("library_global_base");
// Notify the parent workflow that the workflow has finished.
def piId = baseLibrary.getContextValue(context, Constants.CONTEXT_SOURCE_PROCESS_INSTANCE_ID);
def actor = baseLibrary.getContextValue(context, Constants.CONTEXT_ACTOR);
def wfVar = Constants.VARIABLE_STATUS_PREFIX + actor;
WorkflowUtils.addProcessInstanceVariable(piId, wfVar, Constants.STATUS_ENDED);
Example 3: Parent Workflow - Scheduled Check for Completion
This Scheduled node in the parent workflow periodically checks if all child workflows have completed and, when they have, completes the waiting task:
import com.openkm.sdk4j.impl.OKMWebservices;
import com.openkm.sdk4j.bean.*;
import com.openkm.okmflow.util.*;
import com.openkm.okmflow.bean.*;
import com.openkm.util.*;
// Load constants
Class Constants = ScriptUtils.evaluateFromNode("library_request_contants");
//Load libraries
Class baseLibrary = ScriptUtils.evaluateFromNode("library_global_base");
// Load data from the context
long processInstanceId = baseLibrary.getProcessInstanceId(context);
def managerList = baseLibrary.getContextValue(context, Constants.CONTEXT_MANAGERS_LIST);
def actorNum = baseLibrary.getContextValue(context, Constants.CONTEXT_ACTOR_NUM);
def uuid = baseLibrary.getContextValue(context, Constants.CONTEXT_UUID);
def props = [uuid: uuid];
def actorResp = 0;
// When managers completed the sub workflow and task is the waiting task, must complete it
String waitingTask = "Waiting Task";
def taskInstance = WorkflowUtils.getCurrentTaskInstance(processInstanceId);
if (waitingTask.equals(taskInstance.getName())) {
if (managerList != null) {
managerList.each { manager ->
def wfVar = Constants.VARIABLE_STATUS_PREFIX + manager;
if (context.get(wfVar) != null) {
actorResp++;
}
}
} else {
// Never should go into because managerList should not be empty
}
if (actorResp == actorNum) {
// All the managers have answered
WorkflowUtils.setTaskInstanceValues(processInstanceId, waitingTask, null, props);
} else {
// Still waiting for managers
}
}
In this pattern:
- The Action node spawns N child workflows and stores their count and list in the parent's context
- The parent workflow waits at an unassigned task ("Waiting Task")
- Each child workflow notifies the parent by adding a status variable when it completes
- The Scheduled node runs every minute (or at the configured interval), checking if all child workflows have completed
- When all responses are received, the Scheduled node programmatically completes the waiting task, allowing the parent workflow to continue
Context Variables Access
Scheduled node scripts have full access to all workflow context variables. You can:
- Read existing variables using helper methods or directly from the context
- Create new variables by storing values in the context
- Modify existing variable values
- Check for the presence of variables set by child workflows or other processes
Variables are typically accessed through library helper methods (as shown in the examples above) or directly through the workflow execution context object.
Best Practices
- Use descriptive names that clearly indicate the scheduled action's purpose (e.g.,
scheduled_check_approvals,scheduled_sync_child_workflows) - Keep scheduled scripts efficient - they execute repeatedly, so optimize for performance
- Include early exit conditions to avoid unnecessary processing when conditions aren't met
- Be cautious with the execution rate - too frequent execution may impact system performance
- Log important events within scheduled scripts to aid in troubleshooting
- Test the scheduled logic thoroughly to ensure it handles all edge cases
- Document the expected execution frequency and conditions in the Description field
- Consider timeout scenarios - what happens if child workflows never complete?
- Use conditional logic to prevent the scheduled action from executing indefinitely
Scheduled nodes provide a powerful mechanism for implementing complex workflow patterns that require periodic checking, polling, or synchronization. They are essential for coordinating parallel workflows and managing asynchronous operations within the workflow engine.