DEV Community

Explorer
Explorer

Posted on

๐Ÿ”„ Process Migration Between App Versions in Joget

๐Ÿงญ Overview

When upgrading an app in Joget from one version to another, existing running workflow instances might still be tied to the old version.

This BeanShell script automates the migration of running workflow processes from a previous app version (from_version) to a new version (to_version), ensuring smooth continuity without manually aborting or recreating processes.


โš™๏ธ How It Works

Hereโ€™s how this BeanShell script operates inside a Form Store Binder:

  • โš™๏ธ Fetches the submitted data (App ID, From Version, To Version, Process ID).
  • ๐Ÿ” Retrieves all running workflow processes for the specified app_id and from_version.
  • ๐Ÿ“ฆ Loads all available process definitions from the new version (to_version).
  • ๐Ÿ” Compares process definitions between old and new versions.
  • ๐Ÿงฉ For matching process definitions:
    • Migrates the workflow instance to the new version.
  • ๐Ÿšซ For unmatched processes:
    • Aborts them safely to maintain workflow consistency.
  • ๐Ÿงพ Logs every action and updates a num_migrated field with the total migrated count.
  • ๐Ÿ’พ Finally, uses the WorkflowFormBinder to store the updated record.

๐Ÿง  Full Code

import java.util.Collection;
import java.util.ArrayList;
import org.joget.commons.util.LogUtil;
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.model.FormRowSet;
import org.joget.apps.form.service.FormUtil;
import org.joget.apps.form.model.FormStoreBinder;
import org.joget.plugin.base.PluginManager;
import org.joget.apps.app.model.AppDefinition;
import org.joget.apps.app.model.PackageDefinition;
import org.joget.apps.app.service.AppUtil;
import org.joget.workflow.model.WorkflowProcess;
import org.joget.workflow.model.service.WorkflowManager;
import org.joget.workflow.util.WorkflowUtil;

public FormRowSet execute(element, rows, formData) {

    if (rows != null && !rows.isEmpty()) {
        WorkflowManager workflowManager = (WorkflowManager) AppUtil.getApplicationContext().getBean("workflowManager");

        FormRow row = rows.get(0);
        String appId = row.getProperty("app_id");
        String fromVersion = row.getProperty("from_version");
        String toVersion = row.getProperty("to_version");
        String single_processId = row.getProperty("single_processId");

        Collection runningProcessList = workflowManager.getRunningProcessList(appId, null, null, fromVersion, null, null, 0, null);
        Collection processes = workflowManager.getProcessList(appId, toVersion);

        LogUtil.info("BeanShell", "Updating running processes for " + appId + " from " + fromVersion + " to " + toVersion);

        if (!runningProcessList.isEmpty() && !processes.isEmpty()) {
            Collection newProcessDefIds = new ArrayList();
            for (WorkflowProcess process : processes) {
                newProcessDefIds.add(process.getId());
            }

            for (WorkflowProcess process : runningProcessList) {
                String processId = null;
                try {
                    processId = process.getInstanceId();
                    String processDefId = process.getId();
                    processDefId = processDefId.replace("#" + fromVersion.toString() + "#", "#" + toVersion.toString() + "#");

                    if (newProcessDefIds.contains(processDefId)) {
                        workflowManager.processCopyFromInstanceId(processId, processDefId, true);
                        LogUtil.info("BeanShell", "Migrated process " + processId + ".");
                    } else {
                        workflowManager.processAbort(processId);
                        LogUtil.info("BeanShell", "Process Def ID " + processDefId + " does not exist. Aborted process " + processId + ".");
                    }
                } catch (Exception e) {
                    LogUtil.error(getClass().getName(), e, "Error updating process " + processId);
                }
            }

            row.setProperty("num_migrated", runningProcessList.size().toString());
            LogUtil.info("BeanShell", "Completed updating running processes for " + appId + " from " + fromVersion + " to " + toVersion);
        } else {
            LogUtil.info("BeanShell", "No running processes to update for " + appId + " from " + fromVersion + " to " + toVersion);
        }

        PluginManager pluginManager = (PluginManager) AppUtil.getApplicationContext().getBean("pluginManager");
        FormStoreBinder binder = (FormStoreBinder) pluginManager.getPlugin("org.joget.apps.form.lib.WorkflowFormBinder");
        binder.store(element, rows, formData);
    }

    return rows;
}

return execute(element, rows, formData);
Enter fullscreen mode Exit fullscreen mode


`


๐Ÿ’ผ Example Use Cases

โœ… Migrating all running workflow instances when deploying a new app version.
โœ… Keeping workflow states consistent between staging and production environments.
โœ… Automating process transitions during app upgrades or refactoring.


๐ŸŽจ Customization Tips

๐Ÿ’ก Add filtering logic to migrate only specific processes by ID or status.
โš™๏ธ Log more detailed migration statistics (e.g., per-process-type counts).
๐Ÿ” Replace the manual from_version / to_version fields with auto-detected app versions using AppDefinition.


๐Ÿš€ Key Benefits

โœ… Saves hours of manual migration work.
โšก Ensures version continuity without breaking process logic.
๐Ÿงฉ Automatically cleans up invalid or outdated process instances.
๐Ÿ”’ Prevents version mismatches during deployment.


๐Ÿ” Security Note

โš ๏ธ Always validate the app_id and version inputs to prevent accidental cross-app migrations.
โš ๏ธ Avoid exposing internal process IDs in UI or logs in production.
โš ๏ธ Consider restricting this scriptโ€™s execution to admin roles only.


๐Ÿงฉ Final Thoughts

This script provides a robust way to keep your workflow processes synchronized between Joget app versions โ€” perfect for continuous delivery environments or versioned deployments.

Top comments (2)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.