| Red Hat Docs > Manuals > Red Hat Web Application Framework > |
This tutorial describes the components of the simple workflow and how to create workflows and assign them to users and groups. Workflows are based on a dependency graph model, where a task's state changes are dependent on other tasks. A task is a single unit of work and has three states: enabled, disabled, and finished. The states of a task are dependent upon its dependencies (for example, other tasks). A task is enabled if all its dependency tasks are finished. For example, see the diagram below. Assume that Task d is dependent on tasks b and c. Tasks b and c must be finished before task d is enabled.
a
/ |
| |
c b
\/
|
d |
Currently, there two types of tasks: Task and UserTask. The base type task supports commenting on task and does nothing in the events. The second type, user, supports user and group assignments and notifications sent to assignees when a state changes in a task. The user type is a subclass of the base type task, so it also supports task commenting. To add additional functionality to these tasks, you must subclass one of these two task types. Additional functionality involves overriding the events methods: enableEvt, disableEvt, finishEvt, and rollbackEvt. The task state transitions are as follows:
enable -> disable (disableEvt) disable -> enable (enableEvt) enable -> finish (finishEvt) finish -> rollback (rollbackEvt) |
The following steps indicate how to create a hello world workflow and describe the task state change.
Design a workflow by defining the desired tasks and the dependencies between them.
Create a new named task class for the different task types. Be sure to define the BASE_DATA_OBJECT_TYPE, the getBaseDataObjectType, constructors, and clone methods. Also include the methods to invoke.
Write the PDL file to support the new task type.
Create an instantiator for DomainObjectFactory.
Create a workflow template based on the design.
Instantiate a workflow from a workflow template.
Call the workflow start method to start the Engine.
Design your workflow.
Create a new task type by subclassing task:
public class HelloWorldTask extends Task {
//The base data object is used for the domain object
public static final String BASE_DATA_OBJECT_TYPE =
"com.arsdigita.workflow.simple.HelloWorldTask";
protected String getBaseDataObjectType() {
return BASE_DATA_OBJECT_TYPE;
}
//constructors are not inherited from subclasses
//so you'll need to add them
public HelloWorldTask(String label, String description) {
this(BASE_DATA_OBJECT_TYPE);
initAttributes(label,description);
}
public HelloWorldTask(DataObject taskDataObject) {
super(taskDataObject);
}
public HelloWorldTask() {
this(BASE_DATA_OBJECT_TYPE);
setState(DISABLED);
}
public HelloWorldTask(OID oid) throws
DataObjectNotFoundException {
super(oid);
}
public HelloWorldTask(BigDecimal id) throws
DataObjectNotFoundException {
this(new OID(BASE_DATA_OBJECT_TYPE, id));
}
protected HelloWorldTask(ObjectType type) {
super(type);
}
protected HelloWorldTask(String typeName) {
super(typeName);
}
// Add functionality in these methods
public void enableEvt() {
System.out.println(getLabel()+": I am enabled");
}
public void disableEvt() {
System.out.println(getLabel()+": I have been disabled");
}
public void finishEvt() {
System.out.println(getLabel()+": I am finished");
}
public void rollbackEvt() {
System.out.println(getLabel()+": I was rollbacked");
}
//Write clone functionality. Note the use of copyAttributes
//
public Object clone() {
HelloWorldTask taskClone = new HelloWorldTask();
copyAttributes(taskClone);
return taskClone;
}
//Add this method because future versions will
protected void copyAttributes(HelloWorldTask taskClone) {
taskClone.setLabel(getLabel());
taskClone.setDescription(getDescription());
taskClone.setActive(isActive());
}
} |
Ensure that the constructors, events, and clone methods are coded for the new task type.
Write the PDL for the new subclass of task. This is a simple case; you would probably have additional tables for select and DML statements.
object type HelloWorldTask extends Task {
retrieve {
super;
}
insert {
super;
}
update {
super;
}
delete {
super;
}
} |
In your package intializer section, create and add a new instantiator.
DomainObjectInstantiator instHelloWorldTask =
new ACSObjectInstantiator() {
public DomainObject doNewInstance(DataObject
dataObject) {
return new HelloWorldTask(dataObject);
}
};
DomainObjectFactory.registerInstantiator(
"com.arsdigita.workflow.simple.",
instHelloWorldTask); |
Create the workflow template.
public class WFTutorialHello {
public void WFTutorialHello() {
HelloWorldTask helloTask1 =
new HelloWorldTask("hello 1", "description");
HelloWorldTask helloTask2 =
new HelloWorldTask("hello 2", "description");
//Note(1): Save the task before adding the dependency
helloTask1.save();
helloTask2.save();
helloTask2.addDependency(helloTask1);
helloTask2.save();
User user = new User(__USER_ID__)
Workflow wf = new Workflow("hello example",
"tutorial example of creating a task");
wf.addTask(helloTask1);
wf.addTask(helloTask2);
//Note(2): The workflow mad
helloTask1.save();
helloTask2.save();
wf.start();
helloTask1.finish();
helloTask2.finish();
}
} |
There are a couple of subtle issues to note with regard to the above example. (These issues are commented with "NOTE(?)" in the code.) First, it is important to save the task before adding dependencies. Otherwise, the internal persistance will fail. Additionally, ensure that a save is called on each task after adding to workflow, since the workflow may call a couple of methods on the added task.
To add a task, you call the addTask method in workflow. The task is inactive when created, and can be set to be active by calling the setActive(true). A task is not considered part of the workflow until it becomes active. Tasks are created in an inactive state to allow you to preview them before altering the workflow state. Once a task is active, it can affect the workflow and other dependent tasks. Manually setting the task state is required only when adding a new task to an in-progress workflow. When the workflow is started, it will automatically set all task as active.
Rolling back a process to a certain task is done by enabling a task early on in the workflow. For example, to move the process to task A:
//Get reference to task A taskA.enable(); |
When a task is enabled, call the finish method to complete it:
//Get reference to task A and call finish method taskA.finish(); |