1. Why This Script Is Useful
Sometimes a running process reaches a wrong step because of data issues, user mistakes, or production fixes.
This script lets you rollback selected process instances by:
- matching only specific running instances,
- aborting from a known current activity,
- resuming at a target activity,
- restoring workflow variables when possible.
2. Where to Use in Joget
- Workflow Builder context: execute as an admin/maintenance script.
- Best use case: production correction for specific process instances.
- Not a form validator: this operates on workflow engine state.
3. Critical Safety Rules Before Running
- Run only with a strict target instance list.
- Confirm
appId,processDefId, andprocessVersionfirst. - Keep logs enabled and review summary counts after execution.
- Take backup/snapshot before mass correction.
4. Configuration You Must Edit
Update only these values in the script:
appIdprocessDefIdprocessVersionactivityDefIdToAbortactivityDefIdToResumetargetProcessInstanceIds
5. Full Working Code (Unchanged)
import org.joget.workflow.model.service.WorkflowManager;
import org.joget.apps.app.service.AppUtil;
import org.joget.commons.util.LogUtil;
import org.joget.workflow.model.WorkflowActivity;
import org.joget.workflow.model.WorkflowProcess;
import org.joget.workflow.model.WorkflowVariable;
import java.util.Collection;
import java.util.Map;
import java.util.HashMap;
/*
=====================================================================
PURPOSE
=====================================================================
This script performs a controlled workflow rollback / jump in Joget.
- Finds running workflow instances of ONE app + ONE process + ONE version
- Checks if the instance is currently at a specific activity
- Force-aborts that activity
- Starts ANY activity you define (earlier, later, or never-run)
- Restores workflow variables if the target activity ran before
- Re-evaluates assignments after rollback
IMPORTANT:
- This affects ONLY workflow state, NOT form table data
- Designed for admin / production-fix usage
=====================================================================
*/
/*
=====================================================================
GET WORKFLOW MANAGER
=====================================================================
*/
WorkflowManager wm = (WorkflowManager) AppUtil
.getApplicationContext()
.getBean("workflowManager");
/*
=====================================================================
HELPER METHOD:
Get the latest (current) activity of a process instance
=====================================================================
*/
public WorkflowActivity getLatestActivity(String processInstanceId) {
Collection activityList = wm.getActivityList(
processInstanceId,
0,
1,
"dateCreated",
true
);
if (activityList != null && !activityList.isEmpty()) {
return (WorkflowActivity) activityList.iterator().next();
}
return null;
}
/*
=====================================================================
HELPER METHOD:
Get the most recent execution of ANY specific activity definition
(Used to restore workflow variables if activity ran before)
=====================================================================
*/
public WorkflowActivity getLastRecentSpecificActivity(
String processInstanceId,
String activityDefId
) {
Collection activityList = wm.getActivityList(
processInstanceId,
null,
null,
"dateCreated",
true
);
if (activityList != null && !activityList.isEmpty()) {
for (Object obj : activityList) {
WorkflowActivity act = (WorkflowActivity) obj;
if (activityDefId.equals(act.getActivityDefId())) {
return act; // Return the latest matching execution
}
}
}
return null;
}
/*
=====================================================================
HELPER METHOD:
Extract all workflow variables from an activity instance
=====================================================================
*/
public Map extractWorkflowVariables(String activityInstanceId) {
Map vars = new HashMap();
Collection varList = wm.getActivityVariableList(activityInstanceId);
for (Object obj : varList) {
WorkflowVariable var = (WorkflowVariable) obj;
String value = (var.getVal() != null) ? var.getVal().toString() : "";
vars.put(var.getId(), value);
}
return vars;
}
/*
=====================================================================
CONFIGURATION SECTION (MODIFY ONLY THIS PART)
=====================================================================
*/
String appId = "your_app_id";
String processDefId = "your_process_def_id";
String processVersion = "your_process_version";
/* Activity where process MUST currently be */
String activityDefIdToAbort = "your_current_activity_def_id";
/* Activity where process will be resumed (ANY activity you want) */
String activityDefIdToResume = "your_target_activity_def_id";
/* Restrict rollback to specific process instances */
String[] targetProcessInstanceIds = {
"your_process_instance_id"
};
/*
=====================================================================
SAFETY METHOD:
Check if current instance is in target list
=====================================================================
*/
boolean isTargetInstance(String processInstanceId, String[] targets) {
for (int i = 0; i < targets.length; i++) {
if (processInstanceId.equals(targets[i])) {
return true;
}
}
return false;
}
/*
=====================================================================
MAIN EXECUTION LOGIC
=====================================================================
*/
int totalMatched = 0;
int successCount = 0;
int failureCount = 0;
/* Fetch all running workflow instances */
Collection runningProcesses = wm.getRunningProcessList(
appId,
processDefId,
null,
processVersion,
null,
null,
null,
null
);
for (Object obj : runningProcesses) {
WorkflowProcess process = (WorkflowProcess) obj;
String processInstanceId = process.getInstanceId();
/* Get current activity */
WorkflowActivity currentActivity = getLatestActivity(processInstanceId);
/* Skip if not at expected activity */
if (currentActivity == null ||
!activityDefIdToAbort.equals(currentActivity.getActivityDefId())) {
continue;
}
totalMatched++;
LogUtil.info("Auto-Rollback", "Matched instance --> " + processInstanceId);
/* Safety check: only allowed instances */
if (!isTargetInstance(processInstanceId, targetProcessInstanceIds)) {
LogUtil.info("Auto-Rollback", "Skipped (not in target list) --> " + processInstanceId);
continue;
}
/*
===============================================================
Attempt to restore workflow variables from previous execution
of the resume activity (if it exists)
===============================================================
*/
Map wfVars = new HashMap();
WorkflowActivity lastRunOfResumeActivity =
getLastRecentSpecificActivity(processInstanceId, activityDefIdToResume);
if (lastRunOfResumeActivity != null) {
wfVars = extractWorkflowVariables(lastRunOfResumeActivity.getId());
LogUtil.info("Auto-Rollback",
"Workflow variables restored from previous execution of --> "
+ activityDefIdToResume);
} else {
LogUtil.info("Auto-Rollback",
"No previous execution found for --> "
+ activityDefIdToResume
+ ", starting fresh");
}
/*
===============================================================
Force start the chosen activity
===============================================================
*/
boolean started = wm.activityStart(
processInstanceId,
activityDefIdToResume,
true
);
if (started) {
WorkflowActivity resumedActivity =
getLatestActivity(processInstanceId);
/* Restore variables if available */
if (!wfVars.isEmpty()) {
wm.activityVariables(resumedActivity.getId(), wfVars);
}
/* Re-evaluate assignments */
wm.reevaluateAssignmentsForActivity(resumedActivity.getId());
successCount++;
LogUtil.info("Auto-Rollback",
"SUCCESS rollback --> " + processInstanceId
+ " resumed at --> " + activityDefIdToResume);
} else {
failureCount++;
LogUtil.info("Auto-Rollback",
"FAILED rollback --> " + processInstanceId);
}
}
/*
=====================================================================
FINAL SUMMARY LOGS
=====================================================================
*/
LogUtil.info("Auto-Rollback", "TOTAL MATCHED --> " + totalMatched);
LogUtil.info("Auto-Rollback", "SUCCESS COUNT --> " + successCount);
LogUtil.info("Auto-Rollback", "FAILURE COUNT --> " + failureCount);
6. Quick Test Checklist
- Test on one non-critical instance first.
- Verify target instance is currently at
activityDefIdToAbort. - Confirm resumed activity is
activityDefIdToResume. - Check assignment list after
reevaluateAssignmentsForActivity. - Review logs for matched/success/failure counts.
7. Security and Publishing Note
- Replace real app IDs, process IDs, version numbers, and instance IDs with generic placeholders before publishing.
- Do not expose internal process naming conventions in public posts.
8. Final Note
Use this script as a controlled recovery tool, not as a routine workflow path. With strict targeting and logging, it becomes a reliable production-fix pattern.
Follow-up topic: Bulk rollback strategy with dry-run mode and audit report output.
Top comments (0)