6.2. Workflow States

Since processes and tasks are dynamic, interacting entities, their lifecycle needs to be explained in some detail.

Process States

The state chart of a process is shown in Figure 6.2, “States of a process”. After being created, a process is started, and may be suspended and resumed a number of times. Ultimately, the final task is completed and the process closes.

When a process is created, it does not immediately start running. Instead, the process remains "not started" until its start method is invoked. This way, the process' variables can be initialized at leisure. Note that as long as the process is not started, its initial view is active (see Section 6.5, “Workflow Variables and Views”). For example, this allows some variables to be writable only during initialization, and allows different validation rules to apply during setup and during the process' runtime.

When the start method is invoked, the process becomes "running" and starts with its first task. All relevant automated action and user interaction happens during the execution of tasks. The process itself mostly serves to structure and coordinate their execution.

While a process is running, it may be suspended at any time by invoking its suspend method. This stops all progress, be it in automated or user tasks. The process can be continued by invoking the resume method.

A process can terminate either normally, by reaching and completing its final task, or it may be aborted by an invocation of its abort method. Either way, after a short cleanup delay, the process and all its sub processes are destroyed, and all state and variable values are irretrievably lost. If some part of the process' state is still of interest, a process should first be suspended and inspected before aborting it.

States of a process

Figure 6.2. States of a process


Automated Tasks

Now you will learn the lifecycle of tasks.

For simplicity, begin with an automated task. In the normal case, the task's state progresses linearly from left to right, shown in the state diagram in Figure 6.3, “States of an automated task”. The task is started by its process, or by the completion of its predecessor. It waits for its (optional) guard condition to become true. Then the automated actions are executed. When the automated actions are finished, the task becomes "completing". As soon as control is successfully transferred to the successor task, the task enters the "completed" state. The task structure of a process definition may contain loops, so a task that has been executed once may later be reached and start again. A task's lifecycle terminates when the containing process terminates.

Since the guard condition as well as the automated actions can contain customized code, error conditions must be modeled explicitly. When the evaluation of a condition or the execution of an action fails, or if a timer expires, the task is escalated, and will not automatically make any further progress. The previous state before escalation is recorded (denoted as history state (H*) in the state diagram) and can be inquired using Task#getEscalatedState(). If the failure was caused by external circumstances, it may make sense to retry the task after resolving the problem. When the retry method is invoked, the task goes back to the state before escalation and tries to execute the condition or actions once more.

As described above, a process may be suspended. This operation cascades to all tasks contained in the process, which will all be suspended. Each task's state before suspension is recorded (denoted by the lower history state in Figure 6.3, “States of an automated task”), and can be inquired using Task#getSuspendedState(). The task can only continue when the complete process is resumed, which will move each task back to the state before it was suspended.

When a process is aborted, each of its tasks will be marked as aborted (making most methods unusable) and will be destroyed soon after.

States of an automated task

Figure 6.3. States of an automated task


User Tasks

While the execution of an automated task only consists of server-side actions, a user task's execution is split into several steps. As soon as the guard condition is true, a user task is activated, and waits for a user to accept the task. When a user accepts, on the server, the task's preconditions are checked, and the task's entry actions are executed. When the entry actions are finished, the task becomes running, and responsibility for further actions passes to the user. When the user has completed his or her part, the server checks the task's postconditions and runs the task's exit actions.

Figure 6.4, “States of a Task” is a combined state chart for automated and user tasks. Look out for [isUserTask()] conditions which annotate the differences between the task types.

There are several transitions where customized server-side code is executed. In each of these cases, when something goes wrong, the task becomes escalated. Another potential cause for escalation is a timer expiring, for example because the user does not complete a task in the expected period. The mechanism for retry, suspend/resume and abort is the same as described for automated tasks above.

States of a Task

Figure 6.4. States of a Task


When a task is activated, that is its guard check has been passed, it may be offered to several users. By default, all users that have the right to accept the task (see Section 6.6, “The Access Control Service”), and have not rejected the task yet, appear in the set of offered users. A task may also be assigned directly to a user or to a group, or a certain performer may have been forced by a previous task. The strategy for offering tasks to users can be overridden by providing a customized performer policy (see the Workflow Manual for details), or by changing the handling of the accept right in a custom rights policy (see Section 6.10.7, “Rights Policies”).

The set of users a task is offered to may be inquired using the method Task#getOfferedTo(). All tasks that are offered to the current user can be determined using the work list service (see Section 6.4, “The Work List Service”). Changes to a task's set of offered users are signaled by TaskOfferedEvent and TaskRevokedEvent instances (see Section 6.8, “Events”). There are no events for changes to the work list. Instead, when working inside the CoreMedia CAE caching infrastructure, your code simply calls the work list getters, and can rely on the correct dependencies being registered behind the scenes. In this way, your code will be automatically reexecuted when any accessed work list changes. See the CoreMedia CAE Developer Manual and Section 4.10, “Caching” for further details.

A task's guard condition may become false before the task is accepted by any eligible user. In this case, the task goes back to the waiting state.

The user who accepts a task becomes the performer of the task. This entails certain privileges required to perform the task, namely the ability to read and write the task's variables, and the ability to cancel, complete or retry the task.

Before passing control to the user, first, the task's preconditions are checked. This feature can be used to verify assumptions by the workflow designer. If a condition is not met, the task is escalated. If all checks are passed, the task's entry actions are executed. This may include GUI-based remote client actions, which will be executed in the name of the user (see Section 6.10.8, “Remote Client Actions”).

The Unified API offers the method Task#acceptAndEnter(), which waits until the task has safely arrived in the running state. Any exceptions thrown by failing preconditions or entry actions are passed on to the method's caller. This allows for a synchronous programming model: When acceptAndEnter returns normally, you can be sure that the task is running. In contrast, accept supports an asynchronous programming model, insofar as it only triggers the server-side computation. When accept returns, the server-side code may not have finished yet.

A task can be passed directly from one performer to another using the method Task#delegate(). The task remains in the running state, no conditions are checked or actions executed.

A task may also be canceled, sending it back to the activated state. The user ceases to be the task's performer. Again, postconditions are not checked, and exit actions are not executed.

Note that these methods may also be invoked by a different user than the performer, assuming the respective rights are granted. For example, when a user is on vacation and has left behind some running task, an administrator or process owner may still lead the process to conclusion by delegating or canceling the task. An additional option for a user task is to skip the task, in order to make progress even when no suitable performer can be found.

A call to Task#complete() indicates to the workflow server that the user has finished his or her work. All configured postconditions are checked. If any post condition fails, the user probably has not fulfilled his task as planned. The task becomes escalated, and may be retried by the performer, returning it to the running state. Note that the current performer is remembered while the task is escalated and/or suspended.

After all postconditions are successfully checked, the configured exit actions are run, and the task changes to state completing. Similar to acceptAndEnter, the method Task#completeAndExit() synchronously waits until the task including all post conditions and server-side actions has completed, and passes any exceptions on to its caller.

The remaining lifecycle is as described for automated tasks, above.