Parallel Branches. Continued

How to define, use and control the parallel branches that Workflow Engine has carried since version 1.5, and what to watch when several run at once.

Optimajet Team·3 minutes

Parallel Branches. Continued

The sample archive for this article is no longer distributed. The finished scheme is at the end of the walkthrough below, and the current documentation covers the same mechanics in Subprocess and Managing Subprocesses.

Introduction

Since version 1.5 Workflow Engine comes with an ability to define and use forks (parallel processes). Each fork defines a sub-process.

This article explains how to define, use and control parallel branches. You may find an example in the Alternative to Windows Workflow Foundation article.

Warning: the functional doesn’t work with the free license of Workflow Engine. It requires more limits of the scheme. A trial key lifts that limit.

Parallel branches terms

Parallel branches

We added some flags to Transitions for parallel branches.

  • Is fork. Transition marked by this flag starts or ends a sub-process;
  • Merge sub-process via set state. It changes the current activity of the parent process to the final activity of the sub-process and results in merging of a parent process and a sub-process;
  • Disable parent process control. If a parent process is set to an activity where the sub-process cannot exist, it disables the auto-removing of the sub-process.

Create a Scheme

Let’s create the scheme of the process with a root process and two sub-processes.

Commands

  • approve – for direct transition to the main branch;
  • denied – for reverse transition to the main branch;
  • rollback – for sub-process rollback;
  • fastapprove – for sub-process fastapprove.

Root Process

Activities:

  • Draft;
  • MainState1;
  • MainState2;
  • Final.

If you want to create transitions between activities, use the “approve” command for direct transition and the “denied” command for reverse.

Sub-process “RollBack”

This process is used to execute a rollback.

Activities:

  • Rollback.

The process starts with MainState1 activity and it has two transitions:

The incoming transition:

  • Type: AA;
  • Classifier: NotSpecified;
  • Is fork: true.

The outcoming transition:

  • Type: AC;
  • Classifier: Direct;
  • Trigger type: Command;
  • Command: rollback;
  • Is fork: true;
  • Merge sub-process via set state: true.

Sub-process "FastApprove"

This process is used to execute fast approve and to change the state of the main process to Final.

Activities:

  • FastApprove.

The process starts with Draft activity and it has two transitions:

The incoming transition:

  • Type: AA;
  • Classifier: NotSpecified;
  • Is fork: true.

The outcoming transition:

  • Type: AC;
  • Classifier: Direct;
  • Trigger type: Command;
  • Command: fastapprove;
  • Is fork: true;
  • Merge sub-process via set state: true.

As a result, your scheme should look like this:

xml
<Process>
  <Designer />
  <Commands>
    <Command Name="approve" />
    <Command Name="denial" />
    <Command Name="rollback" />
    <Command Name="fastapprove" />
  </Commands>
  <Activities>
    <Activity Name="Draft" State="Draft" IsInitial="True" IsFinal="False" IsForSetState="True" IsAutoSchemeUpdate="True">
      <Designer X="90" Y="270" />
    </Activity>
    <Activity Name="MainState1" State="MainState1" IsInitial="False" IsFinal="False" IsForSetState="True" IsAutoSchemeUpdate="True">
      <Designer X="350" Y="270" />
    </Activity>
    <Activity Name="MainState2" State="MainState2" IsInitial="False" IsFinal="False" IsForSetState="True" IsAutoSchemeUpdate="True">
      <Designer X="650" Y="270" />
    </Activity>
    <Activity Name="Final" State="Final" IsInitial="False" IsFinal="True" IsForSetState="True" IsAutoSchemeUpdate="True">
      <Designer X="910" Y="270" />
    </Activity>
    <Activity Name="RollBack" State="RollBack" IsInitial="False" IsFinal="False" IsForSetState="True" IsAutoSchemeUpdate="True">
      <Designer X="350" Y="80" />
    </Activity>
    <Activity Name="FastApprove" State="FastApprove" IsInitial="False" IsFinal="False" IsForSetState="True" IsAutoSchemeUpdate="True">
      <Designer X="350" Y="400" />
    </Activity>
  </Activities>
  <Transitions>
    <Transition Name="Draft_Activity_1_1" To="MainState1" From="Draft" Classifier="Direct" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="false" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="approve" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer X="313" Y="295" />
    </Transition>
    <Transition Name="MainState1_Activity_1_1" To="MainState2" From="MainState1" Classifier="Direct" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="false" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="approve" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer />
    </Transition>
    <Transition Name="MainState2_Activity_1_1" To="Final" From="MainState2" Classifier="Direct" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="false" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="approve" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer />
    </Transition>
    <Transition Name="Draft_Activity_1_2" To="RollBack" From="MainState1" Classifier="NotSpecified" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="true" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Auto" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer />
    </Transition>
    <Transition Name="BackCall_Draft_1" To="Draft" From="RollBack" Classifier="Reverse" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="true" MergeViaSetState="true" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="rollback" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer X="228" Y="197" />
    </Transition>
    <Transition Name="Draft_Activity_1_3" To="FastApprove" From="Draft" Classifier="NotSpecified" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="true" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Auto" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer X="202" Y="400" />
    </Transition>
    <Transition Name="Activity_1_MainState2_1" To="Final" From="FastApprove" Classifier="Direct" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="true" MergeViaSetState="true" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="fastapprove" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer X="961" Y="454" />
    </Transition>
    <Transition Name="MainState1_Draft_1" To="Draft" From="MainState1" Classifier="Reverse" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="false" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="denial" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer X="311" Y="239" />
    </Transition>
    <Transition Name="MainState2_MainState1_1" To="MainState1" From="MainState2" Classifier="Reverse" AllowConcatenationType="And" RestrictConcatenationType="And" ConditionsConcatenationType="And" IsFork="false" MergeViaSetState="false" DisableParentStateControl="false">
      <Triggers>
        <Trigger Type="Command" NameRef="denial" />
      </Triggers>
      <Conditions>
        <Condition Type="Always" />
      </Conditions>
      <Designer X="589" Y="237" />
    </Transition>
  </Transitions>
</Process>

Test

text
Operation:
0 - CreateInstance
1 - GetAvailableCommands
2 - ExecuteCommand
3 - GetAvailableState
4 - SetState
5 - DeleteProcess
9 - Exit
The process isn`t created.
CreateInstance - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: Draft, CurrentActivity: Draft
Enter code of operation:2
Available commands:
- approve (LocalizedName:approve, Classifier:Direct)
- fastapprove (LocalizedName:fastapprove, Classifier:Direct)
Enter command:approve
ExecuteCommand - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: MainState1, CurrentActivity: MainState1
Enter code of operation:2
Available commands:
- approve (LocalizedName:approve, Classifier:Direct)
- denial (LocalizedName:denial, Classifier:Reverse)
- rollback (LocalizedName:rollback, Classifier:Reverse)
- fastapprove (LocalizedName:fastapprove, Classifier:Direct)
Enter command:fastapprove
ExecuteCommand - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: Final, CurrentActivity: Final
Enter code of operation:4
Available state to set:
- Draft
- MainState1
- MainState2
- Final
Enter state:draft
SetState - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: Draft, CurrentActivity: Draft
Enter code of operation:2
Available commands:
- approve (LocalizedName:approve, Classifier:Direct)
- fastapprove (LocalizedName:fastapprove, Classifier:Direct)
Enter command:approve
ExecuteCommand - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: MainState1, CurrentActivity: MainState1
Enter code of operation:2
Available commands:
- approve (LocalizedName:approve, Classifier:Direct)
- denial (LocalizedName:denial, Classifier:Reverse)
- rollback (LocalizedName:rollback, Classifier:Reverse)
- fastapprove (LocalizedName:fastapprove, Classifier:Direct)
Enter command:approve
ExecuteCommand - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: MainState2, CurrentActivity: MainState2
Enter code of operation:2
Available commands:
- approve (LocalizedName:approve, Classifier:Direct)
- denial (LocalizedName:denial, Classifier:Reverse)
- rollback (LocalizedName:rollback, Classifier:Reverse)
- fastapprove (LocalizedName:fastapprove, Classifier:Direct)
Enter command:rollback
ExecuteCommand - OK.
ProcessId = 'c3b6c1c2-a2b8-4ae0-b208-72351cdc9f43'. CurrentState: Draft, CurrentActivity: Draft

Test it on your own process

The reading ends where a proof of concept begins. Model one process you already run, with a deadline, an escalation, and a restart in the middle of a wait, then count the code that lives outside the definition.