Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications

An essential part of many BPEL processes are the ‘humanual’ steps – process steps that involve inter-human communication, system-bridging actions, decision making, approval and fuzzy logic based operations that we cannot perform automatically. In the case of Oracle BPEL PM, these steps are implemented as calls into the Oracle BPEL PM Workflow Management infrastructure. Tasks are assigned to groups or individual users, email notifications can be sent to alert users to tasks waiting for them and the Workflow Web Application – shipped with Oracle BPEL PM – can be used to access these tasks and process them.

Frequently however, the default Workflow Application will not suffice, for example because the processing of tasks must be integrated into an application already used by the organisation or additional information regarding the context of a certain task from external data sources may need to be presented along with the task. Or perhaps we have developed a background process that has intelligence to pre-process specific tasks. There are several reasons for desiring programmatic access to the Workflow system of Oracle BPEL PM.

In this article we will take a brief look at a simple BPEL Process with a simple Humanual Step – a Workflow task. And we will find out how we can find this task, analyze it and handle it from a remote Java application.....

The ProcessOrderService

Our BPEL Process is the ProcessOrderService. A customer sends an order. The reception of that order triggers the instantiation of a new process instance. The order contains product id, number of items, desired warranty level and the customer id.

The steps in the process are:

  1. takes an order for a specific customer
  2. gets a price quote from the GetPriceQuote service
  3. gets the customer profile from the CustomerProfileService
  4. awards a discount depending on the CustomerStatus
  5. processes the order straight into the Orders Database
  6. returns the total price for the order

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow1 

The steps 2, 3 and 5 require external services to be invoked, to get a Price Quote for the product from a Java based service, gets the Customer Profile from a BPEL Service and records the order in the Orders Database using a SQL based service:

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow2

Introducing Workflow in the process 

We have received an urgent request from our Marketing and Sales department to refine the process. As part of the Customer Care program they have come up the following extension to the ProcessOrder process:

If the order total is over 10k and the customer status is GOLD, let’s try to negotiate a deal:

  • Start workflow in which a human needs to negotiate a deal, update the total price and complete the workflow
  • An email is sent for notification of the employee to start working on the deal-task
  • A Java Web UI is used to present the task details and allow update of Total Price and Task Status
  • When the Workflow completes, the ProcessOrder process resumes, possibly with an updated TotalPrice

Well, adding a bit of workflow to a BPEL process definition is very easy, larger drag & drop and wizard based property definition. In a Switch element, we check for the Customer Status and the Order value and for GOLD and over 10k, we set up a workflow task for the completion of which the process instance will have to wait:

<switch name="Switch_2">
<case condition="bpws:getVariableData('CustomerStatus')='GOLD' and bpws:getVariableData('outputVariable','payload','/client:ProcessOrderServiceProcessResponse/client:totalOrderPrice') &gt; 10000">
<bpelx:annotation>
<bpelx:pattern>GoldAnd10K
</bpelx:pattern>
</bpelx:annotation>
<sequence name="Sequence_7">
<scope name="MakeSpecialDeal" variableAccessSerializable="no" xmlns:taskactionhandler="http://xmlns.oracle.com/pcbpel/taskservice/taskActionHandler"
xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
xmlns="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:bpel="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:taskmngr="http://xmlns.oracle.com/pcbpel/taskservice/taskmanager"
xmlns:task="http://xmlns.oracle.com/pcbpel/taskservice/task"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ora="http://schemas.oracle.com/xpath/extension"
xmlns:wf="http://schemas.oracle.com/bpel/extension/workflow"
wf:key="MakeSpecialDealVar;taskConfigMakeSpecialDeal.xml;SimpleUserActivity;Make deal for Order by customer &lt;%bpws:getVariableData('inputVariable','payload','/client:ProcessOrderServiceProcessRequest/client:customerId')%&gt; - current order total: &lt;%bpws:getVariableData('outputVariable','payload','/client:ProcessOrderServiceProcessResponse/client:totalOrderPrice')%&gt;;bpws:getVariableData('outputVariable', 'payload', '/client:ProcessOrderServiceProcessResponse/client:totalOrderPrice');;;;;">
...

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow3

 

I have configured the task to sent a notification to the user who gets assigned the task.

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow4 

By clicking the hyperlink in the email of by starting the Worklist Web Application, the user John Steinbeck navigates to the task that has been allocated to him. Depending on the specification of the task, he can see data from the BPEL Process Instance, change values and feed them back to the process instance and finally change the status of the task: Close, Escalate, Approve, Deny etc.

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow5

 

Build our own interface for the Oracle BPEL PM Workflow System

With the standard Worklist Application from which we see a screenshot above, we can perform the Humanual tasks that are associated with BPEL process instances. However, as discussed above, we may want a more customized, more sophisticated interface. Or even a programmatic interface.

Oracle BPEL PM provides an interface that we can access either locally (same JVM as the BPEL PM) or remotely (from within another JVM) to inspect and manipulate the workflow tasks.

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow6

The crucial class here is the IDeliveryService Session Bean that is exposed by the BPEL PM Server, via the RMI port. Note: the relevant JavaDoc for the BPEL PM server and client classes can be found here. For specific details on the worklist infrastructure, see here.

To implement a programmtic client of the Oracle BPEL PM Workflow application, I have done the following

< p>1. Create a JDeveloper Application and Proje
ct

2. Add libraries to the project

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow7 

3.  Set up two files: pc.properties and hw_worklist_jndi.properties, in the Classpath (for example in the folder src in our project, to be compiled to the folder classes). The latter file specifies the RMI server where the worklist service can be reached as well as the credentials to be used, the first file sets a number of configuration values for the WorkflowService:

pc.properties:

# Diagnostic Service defaults

oracle.tip.pc.services.identity.endpoint=/integration/services/IdentityService/RemoteIdentityService

# task service related properties
oracle.tip.pc.services.hw.taskservice.ActionableEmailAccount=crm@localhost

# size of transports cached for each email account. Transports are used to send emails
oracle.tip.pc.services.notification.smtp.transport.cache.size = 10

# worklist service related properties
oracle.tip.pc.services.hw.worklist.SessionTimeoutInMinutes=60

#owf service related properties
oracle.tip.pc.services.owf.callback.quartz_interval=1.0

# File Adapter properties
oracle.tip.adapter.file.numProcessorThreads=4

# BPEL-IC Adapter properties
# Repository connection mode (RMI or CORBA)
oracle.tip.adapter.ic.repoConnMode=RMI

hw_worklist_jndi.properties

java.naming.factory.initial=com.evermind.server.rmi.RMIInitialContextFactory
java.naming.provider.url=ormi://localhost/hw_services
java.naming.security.principal=admin
java.naming.security.credentials=welcome
dedicated.connection=true

4. Write the class that will connect through RMI to the BPEL PM and query the worklist for tasks for user JSTEIN

The class that does the actual work is this one:

package nl.amis.sales.crm;

import com.oracle.bpel.client.Locator;
import com.oracle.bpel.client.NormalizedMessage;
import com.oracle.bpel.client.ServerException;
import com.oracle.bpel.client.delivery.IDeliveryService;

import org.w3c.dom.Element;

import com.oracle.bpel.client.Locator;
import com.oracle.bpel.client.util.WhereCondition;


import java.io.IOException;

import java.rmi.RemoteException;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import javax.naming.Context;

import oracle.tip.pc.api.worklist.IWorklistContext;
import oracle.tip.pc.api.worklist.IWorklistService;
import oracle.tip.pc.api.worklist.IWorklistTask;
import oracle.tip.pc.services.hw.task.ITask;
import oracle.tip.pc.api.worklist.WorklistServiceException;
import oracle.tip.pc.api.worklist.payload.Form;
import oracle.tip.pc.api.worklist.payload.PayloadFormGenerator;

import oracle.tip.pc.services.hw.task.impl.Task;
import oracle.tip.pc.services.hw.worklist.RemoteWorklistServiceClient;

import org.w3c.dom.Element;


public class DealNegotiatorClient {
public DealNegotiatorClient() {
}

/** see: http://www.huihoo.com/oracle/docs/B14099_19/integrate.1012/b14448/worklist.htm#BHABGEHG
*/
public static void main(String[] args) throws ServerException, Exception,
WorklistServiceException {
DealNegotiatorClient dealNegotiatorClient = new DealNegotiatorClient();
// Set JNDI Properties for remote invocation ( will work for local also...
// 1. get a handle to the remove worklist service client
RemoteWorklistServiceClient client = new RemoteWorklistServiceClient();

// 2. set approver's user and password
try {
// note: method client.init() uses the files pc.properties and hw_worklist_jndi.properties
// the latter file specifies the RMI server where the worklist service can be reached as well as the credentials to be used
// the first file sets a number of configuration values for the WorkflowService
client.init();
} catch (WorklistServiceException e) {
e.printStackTrace();
}
String user = "jstein";
String password = "welcome";
System.out.println("Connecting to WorklistService as " + user);

// 3. get worklist context for user
IWorklistContext ctx = null;
try {
ctx = client.authenticateUser(user, password);
} catch (WorklistServiceException e) {
// TODO
e.printStackTrace();
}
System.out.println("Got Worklist Context");

// 4. set filters for retrieving My tasks with Assigned status
Map filterMap = new HashMap();

filterMap.put(IWorklistService.FILTER_TYPE_TASK_FILTER,
IWorklistService.TASK_FILTER_MY);
filterMap.put(IWorklistService.FILTER_TYPE_STATUS_FILTER,
IWorklistService.STATUS_FILTER_ASSIGNED);

List tasks =
client.getWorklistTasks(ctx, filterMap, IWorklistService.SORT_FIELD_TASK_TITLE,
IWorklistService.SORT_ORDER_ASCENDING);
if (tasks != null) {
for (int i = 0; i < tasks.size(); i++) {
Task task = (Task)tasks.get(i);
String taskId = task.getTaskId();
System.out.println(task.getTitle());


Element payload = (Element)task.getPayload();
float totalPrice = Float.parseFloat((payload.getNodeValue()));
// automatically award another 13% discount
totalPrice = (float)(totalPrice * 0.87);
payload.setNodeValue(Float.toString(totalPrice));
client.updateTask(ctx, task);
// add some comments to the task
client.appendTaskComments(ctx, taskId,
"Programmatically manipulated by nl.amis.sales.crm.DealNegotiatorClient on " +
new java.util.Date()+ ". New total price set at " +Float.toString(totalPrice));
System.out.println(
"Programmatically manipulated by nl.amis.sales.crm.DealNegotiatorClient on " +
new java.util.Date()+ ". New total price set at " +Float.toString(totalPrice));
// close the task by setting its status to DONE
client.customTaskOperation(ctx, taskId, "DONE");
}

}
}
}

We have programmed the class to find all tasks assigned to the current user. We assume that all tasks are of the type we can handle – which is a crude assumption, probably not valid in a production environment. We retrieve some information about the task – that we write to the system output and can also present in our custom, tailor-made User Interface. Then we manipulate the payload of the task, effectively sending data back to the BPEL process – in this particular case we assign an additional 13% of discount which is sent back through the operation payload.setNodeValue(Float.toString(totalPrice)). Finally we change the status of the task, setting it to DONE. In short our program does what normally the end user would have done in the worklist application interface.

The output looks like: 

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow8

At this point, the BPEL Process will resume processing and the end result is sent back to the requestor.

Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications bpeljavaworkflow9 

 

Resources

Download the JDeveloper 10.1.3 project:  CRMNegotiateDealTaskListClient.zip.

Relevant JavaDoc for the BPEL PM server and client classes can be found here. For specific details on the worklist infrastructure, see here.

2 Comments

  1. Muditha November 10, 2008
  2. Helder Klemp September 23, 2008