Workflow Developer's Guide

By Lars Pind on 30 August 2000.

OpenACS Documentation : Workflow : Workflow Developer's Guide


This document deals with how to use the services of the workflow package in your own package.

What Do I Gain?

Given the formal specification of a workflow, the workflow package will keep track of what tasks are to be performed, notify via email the people that should perform them, and make sure that the same task doesn't accidentally get executed twice by different people.

It'll also provide your users with a unified user-interface to help them keep track the tasks they are supposed to perform. This user-interface also acts as the single place to go for information relating to the task, such as inputs and aids in performing the specific task, a log of all prior activity on the case including comments, and a visual overview over the case.

It also provides you with an interface for analyzing the performance of your workflow. You'll be able to spot bottlenecks, find overdue tasks, etc.

Finally, as the package evolves based on input from you and other users, the features above will grow stronger, and more will be added (see future plans).

What Does It Take?

The workflow process is centered around an object. In an insurance company, that could be an insurance claim; if you're writing the ticket-tracker, it would be the ticket itself; etc. So you need to define that object first, creating tables, etc., as necessary.

From there on, there are four steps involved in taking advantage of the services of the workflow package in your application.

  1. Define your process. You need to formally specify your process definition. This is as simple or as hard as the actual process you're dealing with. If the process for your particular application is simple, then formalizing it for use with the workflow package is going to be simple. If the process for your application is complex, with lots of rules, then formalizing it is going to be complex. There is a very easy-to-use web-based interface to help you formalize processes.

  2. Customize the user-interface. The task page can have any number of panels, in addition to the one "Action" panel provided by the system. You must write these subtemplates and include them in your package, then provide the URL and a header in your process definition.

  3. Add a mechanism for starting cases. You'll need some way to start cases. Typically, you'll want a trigger, so that every time a row is inserted into your "tickets" or "claims" table, a new case is started. Or you may want to do this at the API level, so that the function that creates a new "ticket" or "claim" object also starts a new workflow case around it.

  4. Integrate the user-experience. Your package probably wants some admin pages, as well as some end-user pages. These should be integrated with the user-interface offered by the workflow package, by providing links to the relevant workflow pages.
For more complex processes, you may additionally need one or more of these steps:

Defining Your Process

This is pretty simple. You simply use the web-based user-interface i.e., any combination of the Simple Process Wizard and the Advanced Process Builder, to define your process.

When you're happy with your process and have tested it, you click on export process from the process page, to export the process to a SQL script file, which you can include in your distribution. But don't do this just yet. Make sure you've completed all of the steps below before you export it.

Customize the User-Interface

The information provided by default on the task page is somewhat shallow. You will want to supply your own sub-templates to this page to display information specific to your object of concern.

While defining your process, you can define the panels for each of the tasks. The panels consist of a header and the URL of the subtemplate you want to provide the contents of the panel. The URL is, due to the way the templating system works, not a real URL. Rather, it's a path to the file to include, and will typically look like this: /packages/your-package-name/www/your-panel-template-name e.g., /packages/insurance-claim-app/www/claim-info. Then, in the location in your package, you must provide at least the claim-info.adp, and most likely also the claim-info.tcl files.

Add a Mechanism for Starting Cases

If there must be a process for each and every object that your application is dealing with (claim reports, articles, tickets, etc.), then the smartest way of implementing this is to start a process case from the same API call that creates a new object of your type, like this:
create or replace package body ticket
is
    function new (
        ...
    ) return integer
    is
        v_ticket_id number;
	v_case_id number;
    begin
        /* Create the ticket object */
        v_ticket_id := ... ;

	v_case_id := workflow_case.new(
	    workflow_key => 'ticket_wf',
	    object_id => v_ticket_id,
            ...
        );
 	
	workflow_case.start_case(
	    case_id => v_case_id,
            ...
        );
	
        return v_ticket_id;
    end;

    ...
end ticket;
You can also do this from an after insert trigger on the table holding your objects.

If, however, only some of your objects give rise to a process case, then you're better off creating a UI page for starting a case. The workflow API calls to use are the same as in the above.

Integrate the User Experience

This basically consists of providing links from the relevant pages of your application to the relevant pages within the workflow package. It's reasonably safe to assume that the workflow package is mounted at /workflow/. If you want to be super-general, you'll have to query the sitemap tables for the correct URL stub.

The task page, specifically, takes a return_url argument, so you can redirect to this page and expect the user to be able to find his way back to your page.

What are Callbacks?

Callbacks are a mechanism for the Workflow Engine to execute code that you supply. You specify to the workflow engine the name of the PL/SQL function or procedure you want invoked, and the Workflow Engine invokes it with a pre-specified argument list. In other words, the procedure or function must have exactly the signature specified by the Workflow Engine, but apart from that, it can do anything.

The principle for callback arguments is that it takes the minimum list of arguments necessary to fully qualify what transition or arc triggered it, plus a custom argument, the value of which is specified during the process design. The custom argument is there, so you can parameterize your callback. Say you have a callback that returns the a date one week from now. If you need another callback returning the date two weeks from now, you don't have to write a new PL/SQL function. You can supply one that takes the number of days as its custom arg, and returns the date custom arg days from now.

The signatures are specified in Appendix A.

Adding Guard Callbacks

I assume you know what a guard is and what it does; otherwise, read the Conceptual Guide now. There is one special guard, default, denoted by a hash (#) symbol, which evaluated to true when all non-default guards evaluate to false. This is useful, because you normally want to make sure that exactly one of the guards on arcs going out of the same transition evaluate to true. It corresponds to the 'else' clause in an if-statement, or the 'default' clause in a switch statement.

Apart from this, there is one guard callback function included in the pacakge, namely wf_callback.guard_attribute_true, which returns true if the attribute, whose name is given in the custom_arg argument evaluates to true. This is probably one of the most common guards you'll encounter.

A guard callback must follow the signature given in appendix A, and return either 't' or 'f'. It will typically query some workflow attribute, but apart from that it may do whatever it wants, although it generally shouldn't have side-effects.

Adding Action

If you want an automatic transition, or any other transition, for that matter, to have side-effects of any kind, you can specify an action to be called either when the transition is enabled or when it fires.

An action is also a callback, following the signature given for enable or fire callbacks below. Actions don't by themselves alter the state of the process, they only have side-effects. They can, however, set workflow attributes, which can then be queried by guards.

You can use this to implement a process that doesn't require human intervention. If you have a process, such as billing a credit card, where a number of operations must happen in some non-trivial order, you can model the order and the dependencies in a workflow process, and have the actions performed automatically. By doing it this way, you separate the logic governing the overall process from the technicalities of actually performing the actions.

Other Callbacks

There are a number of places where you can supply a callback to control the operation of the workflow engine in minute detail. These are:
Assignment
Who is assigned to a task may depend on some application-specific details. By supplying an assignment callback for a transition, your application can determine the assignment at run-time. This callback will get executed each time the transition becomes enabled, and should use the workflow_case.add_task_assignment to set the resulting assignment for the task.
Time
Timed transitions are automatic transitions that trigger at some pre-specified time. You can supply a callback to compute the time that the transition should trigger. This callback will be executed each time a timed transition becomes enabled, and should return an Oracle date. At the date and time specified by this returned date, the transition will automatically fire. For this, as well as for the two following situations, the predefined wf_callback.time_sysdate_plus_x callback function comes in handy. Specify the number of days as the custom_arg. You may specify fractions of a day, by specifying a decimal number.
Deadline
Tasks may have deadlines. In order to compute the deadline, a callback can be executed, which must return the deadline as an Oracle date. Alternatively, if the deadline is to be manually specified in another task, that other task should set a workflow attribute of type date, and you specify that the transition should use this attribute as its deadline date. The date used is the value of the attribute as the transition becomes enabled. If the attribute value change, the deadline of the enabled transition does not change, but if the transition becomes enabled again, the new value will be used. See note on wf_callback.time_sysdate_plus_x above.
Hold Timeout
When a user starts a task, he obtains a lock on that enabled transition, and the tokens it will consume. But you might not want a user to hang on to a task forever without finishing it. Thus, you can supply a hold timeout date, which is similar to a deadline. When this date comes, if the user is still holding on to the task, the task will automatically be canceled, and thus become available for other users to start. This callback will get called as the user starts the task. See note on wf_callback.time_sysdate_plus_x under Time above.
Notification
The notification callback will get called whenever a user is assigned to this transition. The notification callback is passed the default subject line and body, the recipient party, and the default sending party. It may modify any of these elements before posting the message, or may choose to ignore the request and return without posting at all. If no notification callback is provided, the party assigned to the task will be notified automatically by the workflow engine.

In ACS 4.x Classic, the callback procedure did not have to post the message, but rather could modify the subject, body and sending party directly through the use of PL/SQL in out parameters. PL/pgSQL does not support in out parameters, so the semantics were changed to work as described above.

Unassigned task
Whenever a transition is enabled, but there are no assignees, this callback will get called. You will typically use this to notify some principal that there's an unassigned task that he will want to take a look at.

Message Transitions

Message transitions are used when the something outside of the workflow system triggers the transition e.g., the arrival of an email. What you'll need to do is write the code necessary to receive that email, and the call the workflow_case.fire_message_transition API procedure to trigger the transition.

You're Ready To Go!

This concludes our tutorial on everything you need to know to harness the powers of the workflow package in your own applications. Good luck.

If you get stuck, don't forget where you can find the support that you need.

Appendix A. Callback Signatures

Guards
function name(
    case_id           in number, 
    workflow_key      in varchar2, 
    transition_key    in varchar2, 
    place_key         in varchar2, 
    direction         in varchar2, 
    custom_arg        in varchar2
) return char(1);
Return 't' for true and 'f' for false.
Transition Enable/Fire
procedure name(
    case_id           in number, 
    transition_key    in varchar2, 
    custom_arg        in varchar2
);
Task Assignment
procedure name(
    task_id           in number, 
    custom_arg        in varchar2
)
This procedure should use workflow_case.add_task_assignment to assign a user to the task.

Timed Transition Trigger Time
function name(
    case_id           in number, 
    transition_key    in varchar2, 
    custom_arg        in varchar2
) return date;
Return the date the transition should automatically fire.
Task Deadline Date
function name(
    case_id           in number, 
    transition_key    in varchar2, 
    custom_arg        in varchar2
) return date;
Return the deadline date.
Task Timeout Date
function name(
    case_id           in number, 
    transition_key    in varchar2, 
    custom_arg        in varchar2
) return date;
Return the date the user's lock should timeout.
Notification
procedure name(
    task_id           in number, 
    custom_arg        in varchar2, 
    party_to          in integer, 
    party_from        in integer, 
    subject           in varchar2, 
    body              in varchar2
);
The party_from, subject and body arguments will contain the default values, that the callback can modify as it wishes.
Unassigned Task
procedure name(
    case_id           in number, 
    transition_key    in varchar2,
    custom_arg 	      in varchar2
);

lars@pinds.com
Last Modified: $Date: 2002/07/09 17:35:00 $