DEV Community

Explorer
Explorer

Posted on • Edited on

🔄 Process Migration Between App Versions in Joget

1. Why This Is Useful

When a new app version is deployed, some process instances may still be running on an older version.

This script migrates running instances from one version to another and handles missing target process definitions safely.

2. Where to Use in Joget

  • Form Binder script for admin migration forms.
  • Maintenance workflows used by platform administrators.
  • Controlled production migration tasks after version deployment.

3. Inputs Required

The script reads these submitted form fields:

  • app_id
  • from_version
  • to_version
  • single_processId (available in script; optional strategy)

4. What the Script Does

  • Reads running instances for the source app/version.
  • Loads process definitions from the target version.
  • Builds allowed target process definition IDs.
  • Migrates each process with processCopyFromInstanceId when valid.
  • Aborts process if mapped target definition is missing.
  • Stores migration count in num_migrated.

5. Full Working Code

https://dev.to/exploringmylifeworks/process-migration-between-app-versions-in-joget-bok


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) {

    //check the rows is not empty before run it
    if (rows != null && !rows.isEmpty()) {
        WorkflowManager workflowManager = (WorkflowManager) AppUtil.getApplicationContext().getBean("workflowManager");

        //Get the submitted data
        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 runningProcessList = workflowManager.getRunningProcessList(appId, single_processId, 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);
        }               

        //Reuse Workflow Form Binder to store data
        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

6. Quick Validation Checklist

  • Confirm from_version and to_version are correct.
  • Validate target version contains required process definitions.
  • Test first with a small controlled process set.
  • Verify num_migrated and logs after execution.

7. 📘 Log Headings to Watch

Use these log headings so admins can quickly understand migration results:

  • Updating running processes for ... -> migration started.
  • Migrated process ... -> one process migrated successfully.
  • Process Def ID ... does not exist. Aborted process ... -> target mapping missing.
  • Completed updating running processes for ... -> migration finished.
  • No running processes to update for ... -> nothing matched for migration.

8. Security and Publishing Note

  • Replace real app IDs, versions, and process IDs with placeholders in public posts.
  • Avoid exposing internal release/versioning strategy details.

9. Final Note

This is a practical maintenance pattern for safe process migration during version upgrades, especially in active production environments.

Follow-up topic: Dry-run migration mode with preview and report output.

Top comments (2)

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