Workflow automation in AJAX applications: zkWorkflow

Defining complex business logics in a web application by hard-coding the response to user interaction in the web application itself can become very hard to maintain and modify. Conversely, workflow systems allow to decouple the application logics from its interface, but they contain no UI definition.

We have created simple ZK components that make it possible to define AJAX user interfaces independent from the workflow of the application. The main component loads and starts a workflow definition, and pairs ZK components to specific status in the workflow. Workflow actions can be controlled by UI users by means of simple trigger components, and transitions of state are communicated to all UI components through ZK events.

An application that uses zkWorkflow plugin is typically made of a zul file that defines the interface, and a workflow document defined using our enhanced version of the OpenSymphony workflow library. All workflow components are windows that wrap other components and activate, show and send events to them to control and react to an ongoing workflow.

In order to create an application you need to:

  1. Create your workflow, either manually or using the OpenSymphony graphical editor. You can use special meta-annotations to define status messages to display and variables to compute, as explained here. Workflows that define an application can always be tested without the GUI, using a text-based, non-GUI debug director that we provide.
  2. Define the interface using the workflow ZK components and set the workflow URL as the workflow parameter of the top-level workflow-application component.

The following components are all you need to know about. You will probably also want to check out the demo on this site, which contains full source code examples for both the workflow and the interface.

zkWorkflow components


The whole interface should be contained in a top-level <workflow-application> component. This is derived from the zk window, so it accepts all its parameters plus a workflow attribute that specifies the URL of the workflow to run. The workflow is automatically started when the application is created.

Step interface: workflow-step

For each step that corresponds to a workflow step where user interaction is possible, you should create a workflow-step component that provides the GUI to present to the user when the step is reached. The step is defined by providing the step parameter, which must correspond to the workflow step's name. The match will tell zkWorkflow that when the step is encountered, the component must become active one. This component is also a window, and it will be invisible until the corresponding status is reached, unless the attribute keepVisible="true" is used. If more than one component is available with the same step id, they will all be activated. If a split state is reached in the workflow, all the step components for the active steps are activated.

The first visible workflow-step will be the one that corresponds to the step that the initial action of the workflow transitions to. You can choose to place all the steps in a vbox and make them all the same size; because only one is visible at one time, the effect will be of each step interface to substitute the other, as you can see in this demo. You can also arrange them differently, using keepVisible in some of them so that they remain there.

Any time an action is triggered and a status transition happens, all components that listen to the "onTransition" event will be notified of it. The onTransition event carries the action that produced the transition as data; the WorkflowAction API can be used to retrieve any variable that were set into it by the triggering components (or by the workflow itself). Note that these components can be of any type, and the only requirement for notification is that they are children (at any level) of the main workflow-application. The variables follows the rules laid out in our extended version of osworkflow.

Configurable action triggers: workflow-trigger

When workflow steps contain actions that should result from user interaction, define as many <workflow-trigger> components WITHIN the <workflow-step> as you want to control. The action attribute of the workflow-trigger must be the same as the action name. These are also windows, and will be visible  when the step window is.

In order to allow each trigger to control the workflow, you must create UI elements such as buttons or input fields in them, and define the relevant events (e.g. onClick) to invoke advanceWorkflow() on the trigger (typically the parent component). If you want the action that is sent to the workflow to contain data (e.g. username/password), you can set any object in the action by passing parameters to advanceWorkflow, in name/value pairs: e.g.

advanceWorkflow("username", getUsername(), "password", getPassword()).When the relevant event is triggered by the user, the action that is controlled by the trigger is sent to the workflow, which in turn triggers a state transition. This redefines the content of the workflow-application appropriately.Note that you should add action triggers for all the actions in a step that you want the user to control. If due to validation or other workflow states the action is not available at a particular time, the trigger will do its best to disable all the child components that can be disables, such as buttons, input fields etc.

Predefined trigger components

To make it simple to define triggers of common use, some predefined components are provided: button-trigger and toolbarbutton-trigger for now. They are exactly like the non-trigger ZK counterpart, but take the additional action parameter that makes them react to user activation by advancing the workflow with the given action. They will be disabled by default unless the action can be performed in the current state. See the example.

Status messages

All children (at any level) of workflow-application are automatically notified of status messages coming from the workflow if they are programmed to react to onStatusMessage. If so, the event they receive will contain the value of the "status.message" meta annotation set in the actions, and will be sent any time that an action is started and before any pre-functions are called. After the action has determined a transition, and before any step transition is communicated to the UI, another event will be posted with an empty string as data, so that status messages can be reset.