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

PropertyTypeDescription
Name Text

The name must begin with the prefix scheduled_ followed by a descriptive name (e.g., "scheduled_wait_for_child_workflows", "scheduled_check_completion"). The prefix tells the workflow engine to treat this node as a scheduled action.

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:

  • Textarea: Edit directly in the textarea field
  • Popup editor: Click the icon above the textarea to open a larger editing window

The editor supports autocomplete functionality. Press Ctrl+Space while typing to display available options and code suggestions.

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:

  1. A parent workflow starts and reaches an Action node that spawns multiple child workflows in parallel
  2. The parent workflow then waits at a task (typically unassigned) while the child workflows execute
  3. Each child workflow, upon completion, notifies the parent by setting a variable in the parent's context
  4. A Scheduled node in the parent workflow periodically checks if all child workflows have completed
  5. When all child workflows are finished, the Scheduled node completes the waiting task programmatically
  6. 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.