11.4. Workflow Tutorial

This tutorial covers various tasks that can be accomplished with the workflow service — a simple workflow, adding a task, rolling back a task, and completing a task.

11.4.1. Simple Workflow

These steps describe the procedure in Procedure 11.1, Creating a Simple Workflow. The procedure covers creating a hello world workflow and describing the task state change.

  1. Design a workflow by defining the desired tasks and the dependencies between them.

  2. 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.

  3. Write the PDL file to support the new task type.

  4. Create an instantiator for DomainObjectFactory.

  5. Create a workflow template based on the design.

  6. Instantiate a workflow from a workflow template.

  7. Call the workflow start method to start the Engine.

Creating a Simple Workflow

  1. Design your workflow.

  2. 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.

  3. 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;
        }
    
    }
  4. In your package initializer 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);
  5. 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(n) in the code.) First, it is important to save the task before adding dependencies. Otherwise, the internal persistence 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.

11.4.2. Adding a Task to a Workflow

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 tasks as active.

11.4.3. Rolling Back a Task

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();

This will set the workflow back to the state that preceded finishing task A.

11.4.4. Completing a Task

When a task is enabled, call the finish method to complete it:

//Get reference to task A and call finish method
taskA.finish();