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.
- 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.
- 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.
- 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.
- 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:
- Add guards callbacks. If your process
contains complex conditional behaviour, chances are that you will need
to program some PL/SQL. Guards execute an arbitrary PL/SQL function
with a predefined signature. A few standard guard functions are
already included, but if you need more control, you'll have to supply
your own.
- Add action callbacks. A process can execute when a
transition fires, when it becomes enabled, when notifications are to
be sent, etc. Just like with complex guards, you write a PL/SQL
procedure or function with a pre-defined signature, and supply the
name of the function for the action.
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/02/11 07:45:52 $ |