The following is a simple demonstration of the workflow automation extensions for ZK. The mini-application below has been created with just a ZUL file and an osworflow XML file. As you can see, the simple application logics is defined in the workflow document. The zul interface simply provides interfaces for individual steps and triggers for actions. The zkWorkflow extensions make the operation very simple.
Here is the ZUL interface that generates this example:
<?xml version="1.0" encoding="utf-8"?> <workflow-application xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns="http://www.zkoss.org/2005/zul" workflow="http://localhost:8080/OLmapDemo/simple.xml" id="wapp" width="100%" border="none"> <vbox width="100%"> <groupbox width="400px" height="330px"> <caption label="Workflow interface"/> <!-- interface components for the three possible steps. Only one of them will be visible at one time. This can be overridden with the keepVisible attribute. Otherwise they're simply windows. --> <workflow-step step="sayHello" width="100%" height="330px"> <vbox width="100%"> <div>This component handles the "sayHello" state of the <xhtml:a href="simple.xml"> simple.xml </xhtml:a> workflow. You are seeing it because the workflow's initial action brought you there. There are two other possible states, each with its workflow-step components. The workflow-application only visualizes the GUI components assigned to the current step, unless you override this behavior with keepVisible = true.</div> <div>In this state, you can choose to perform two actions, one that computes the current time, and another that computes a simple string.</div> <div>The buttons below are generated by simply creating two button-trigger components and passing the respective action names to them. Alternatively, you can create triggers as complex as you like by wrapping ZUL components in a worflow-trigger component and calling advanceWorkflow as a response to user events.</div> <separator /> <!-- triggers for actions that are relevant to this step. If you don't have the triggers, you can still advance the workflow programmatically. --> <hbox> <button-trigger action="tellTime" label="Tell time" /> <button-trigger action="sayName" label="Say name" /> </hbox> </vbox> </workflow-step> <workflow-step step="showTheTime" width="100%" height="330px"> <vbox width="100%"> <div> This component handles the "showTheTime" step. The action that leads into this step is defined to calculate the current date and set it in the action variable "time" every time it is executed. The label below is set to react to a state transition by looking for the value of the "time" variable in the action that caused it, and setting it as the label text. You should see the time when you clicked the action button that sent you here. </div> <separator /> <label> <!-- the "data" field has been set in the action by the workflow. Note that other transitions will also invoke this one with a null value, so you want to make sure that it's fine to have null as a result of getActionData. --> <attribute name="onTransition"> self.setValue(event.data.getVariable("time")); </attribute> </label> <button-trigger action="goBack" label="Go back" /> </vbox> </workflow-step> <workflow-step step="showMyName" width="100%" height="330px"> <vbox width="100%"> <div> This component handles the "showMyName" step. The action that leads into this step defines the "name" variable. The label below simply shows it. Variables can be set by actions, steps, or globally in workflows. The variable resolver looks for a name in the action, then in the current steps, then in the workflow. </div> <div> Defining variables in steps with the same name is a simple way to track changing state in workflows. The variables are evaluated by MVEL code in the same JVM that runs your application and can be of any Java type. You can also define variables explicitly in event handlers. </div> <separator /> <label> <!-- name is another workflow-computed variable defined for the action that brings us here. --> <attribute name="onTransition"> self.setValue(event.data.getVariable("name")); </attribute> </label> <button-trigger action="goBack" label="Go back" /> </vbox> </workflow-step> </groupbox> <!-- will display status message when an action that has it starts, and resets it when it ends. Useful for long-running actions, e.g. those that call another workflow or perform complex calculations. --> <label width="100%" value="Status here" id="status" onStatusMessage="status.setValue(event.data)"/> </vbox> </workflow-application>
Here is the workflow, generated with the OpenSymphony graphical workflow editor.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.8//EN" "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd"> <workflow> <meta name="lastModified">Thu Mar 13 14:15:45 EDT 2008</meta> <meta name="created">Thu Mar 13 14:11:13 EDT 2008</meta> <meta name="generator">OSWorkflow Designer</meta> <initial-actions> <action id="0" name="Start Workflow"> <results> <unconditional-result id="4" old-status="Finished" status="Queued" step="1"/> </results> </action> </initial-actions> <steps> <step id="1" name="sayHello"> <actions> <action id="5" name="tellTime" view="sayHello"> <meta name="status.message">Setting current time</meta> <meta name="set.time">new java.util.Date().toString()</meta> <results> <unconditional-result id="6" old-status="Finished" status="Queued" step="2"/> </results> </action> <action id="7" name="sayName" view="sayName"> <meta name="status.message">Setting name to Jack</meta> <meta name="set.name"><![CDATA["my name is Jack"]]></meta> <results> <unconditional-result id="8" old-status="Finished" status="Queued" step="3" display-name="tellName"> </unconditional-result> </results> </action> </actions> </step> <step id="2" name="showTheTime"> <actions> <action id="11" name="goBack" view="goBack"> <results> <unconditional-result id="12" old-status="Finished" status="Queued" step="1"> </unconditional-result> </results> </action> </actions> </step> <step id="3" name="showMyName"> <actions> <action id="9" name="goBack" view="goBack"> <results> <unconditional-result id="10" old-status="Finished" status="Queued" step="1"> </unconditional-result> </results> </action> </actions> </step> </steps> </workflow>