Wednesday, February 29, 2012

OBPM 11g And Human Workflow Services

In this posting I describe a case in which I demonstrate the working of the Human Workflow Services of the Oracle BPM Suite 11g. Or rather the SOA Suite 11g, as it is used for human workflow in BPEL as well.

But before I do so, a brief explanation of what the Human Workflow Services is.


So What is it?

It probably is the most used part of what also is known as the SOA Suite API. The Human Workflow Services provide services to, how surprising, handle human tasks. But there also is an API for Oracle Business Rules, B2B, and the User Messaging Services, to mention a few others.

Ever worked with the OBPM's Workspace or Worklist? Just realize that practically all data you see in there and all actions you can perform are provided by the Human Workflow Services. If you ever need to provide a custom worklist, for example to embedded it in some 3rd party application, the Human Workflow Services is what you will be using.

The most commonly used services probably are the following:
  • TaskQueryService To provides operations to query tasks, based upon keywords, category, status, business process, attribute values, etc.
  • TaskService Provides single task operations, to manage task state, to persist, update, complete, reassign, or escalate tasks, etc.
  • IdentityService To authenticate users (when loggin on into the workspace), to lookup user properties, roles, group memberships, etc.
Other services are the TaskMetaDataService, UserMetaDataService, TaskReportService, RuntimeConfigService, and TaskEvidenceService.

Except for the IdentityService all other services have both a SOAP as well as a remote EJB interface. The IdentityService is a thin SOAP layer on top of WebLogic.


How to Use It?

All services require authentication, except for the IdentityService (obviously). Authentication is based upon a user name/password combination or a previously determined login token. It is a best practice to only authenticate the user once per session, using a user name/password combination. The engine will instantiate a workflow context (similar to a servlet context) and returns a token that references the workflow context. This token can then be used in successive calls to the API. This prevents that with every call a new workflow context needs to be instantiated, which is a relatively expensive operation.



It is also a best practice to use identity propagation of an already logged on user, which in case of SOAP can be done by passing on a SAML token. All SOAP services (except for the IdentityService) have two different end points: one that requires a SAML token and one without. To prevent getting frustrated because of problems with authentication, be aware that when using a tool like soapUI the default end point probably is the SAML one. If you cannot use identity propagation, you will be using the TaskQueryService.authenticate operation to get the workflow context token.

A typical pattern to execute a human task, would be as follows:
  1. Requiring a token using the TaskQueryService.authenticate operation,
  2. Getting a list of tasks based upon some filter using the TaskQueryService.queryTasks operation,
  3. Acquiring (locking) a specific task for execution using the TaskService.acquireTask operation,
  4. Updating the payload of the task using the TaskService.updateTask operation,
  5. Setting the outcome (commiting) the task using the TaskService.updateOutcome operation.
Hereafter I will give an example of this 5-step pattern to execute a human task.

The addresses of the WSDL's are not too consistent, I suppose to prevent us from becoming lazy. The address of the TaskQueryService WSDL is:

http://host:port/integration/services/TaskQueryService/TaskQueryService?WSDL

The address of the TaskService WSLD is:

http://host:port/integration/services/TaskService/TaskServicePort?WSDL


Executing a Human Task Example

Step 1

To get a workflow context token, the following request would give me one for the user "cdoyle" with password "welcome1":



The response will be a <workflowContext> element with a <token> in it that can be used in successive calls.


Step 2
Now I can query tasks for the user cdoyle. Let's assume I want to query all tasks that either are assigned to the group cdoyle belongs to or explicitly have been assigned to cdoyle. I can use the TaskQueryService.queryTasks operation with the following query (the simplest I can think of):



As you can see the token has been copied into the request. To get all tasks I have set the <taskPredicateQuery> attributes startRow and endRow to 0. I could also have left these attributes out all together. The startRow and endRow attributes support paging of tasks, which can be handy in case of a custom worklist. The response is a <taskListResponse> with a list of <task> elements. Each <task> element has subelements like <title>,
<priority>, and <taskId>.


Step 3

The <taskId> uniquely identifies the task instance, and is used in the next call to the TaskService.acquire operation, to lock a task, for example:



The response of this operation will contain a <payload> element which is of type anyType and contains the human task payload as configured in the process. In my example the payload concerns a (how original!) a sales

<QuoteReqest> element:



The response also contains a <state>, <substate> and <taskId> elements, that will show that the task has been assigned and acquired:


Step 4

Now let's assume that this is the payload of a Review Quote task, and that I want execute that task by updating the quantity to 2 and setting the status from "new" to "OK".

To update the payload of the task, I use the TaskService.updateTask operation. This has a lot of attributes. I will keep it simple, meaning that I don't add attachments or comments, etc., so for me it suffices to copy and paste the complete <task> element from the response of Step 3 into the <task> element of the request:


In its response it will give the updated payload, and (among a lot of other things) it will say who did the update and when:


Step 5

To commit the task, I use the TaskService.updateOutcome operation. You will also find that this operation has a lot of attributes. I will keep it simple again, and copy and paste the complete <task> element from the response of Step 4 into the <task> element of the request of the TaskService.updateOutcome operation (left out a lot of the elements for reasons of brevity):



If you watch carefully, you will see that the <taskId> is both in the <systemAttributes> subelement of <task> as well as a separate <taskId> element of its own (at the bottom). The latter one is used to determine the task instance.

The value I entered for outcome, has to correspond with the possible outcomes I defined for the Human Task definition:



In its response this operation will give back the changed payload, plus a number of other elements, among them the <state> element that will have the value COMPLETED now:


hAPI programming!