Programmatically Accessing the Oracle BPEL PM Workflow Tasklist from Java Applications

2

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

 

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:

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:

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

 

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

 

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.

 

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.

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

 

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<br /><br />oracle.tip.pc.services.identity.endpoint=/integration/services/IdentityService/RemoteIdentityService<br /><br /># task service related properties<br />oracle.tip.pc.services.hw.taskservice.ActionableEmailAccount=crm@localhost<br /><br /># size of transports cached for each email account. Transports are used to send emails<br />oracle.tip.pc.services.notification.smtp.transport.cache.size = 10<br /><br /># worklist service related properties<br />oracle.tip.pc.services.hw.worklist.SessionTimeoutInMinutes=60<br /><br />#owf service related properties<br />oracle.tip.pc.services.owf.callback.quartz_interval=1.0<br /><br /># File Adapter properties<br />oracle.tip.adapter.file.numProcessorThreads=4<br /><br /># BPEL-IC Adapter properties<br /># Repository connection mode (RMI or CORBA)<br />oracle.tip.adapter.ic.repoConnMode=RMI<br /><br />

hw_worklist_jndi.properties

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

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;<br /><br />import com.oracle.bpel.client.Locator;<br />import com.oracle.bpel.client.NormalizedMessage;<br />import com.oracle.bpel.client.ServerException;<br />import com.oracle.bpel.client.delivery.IDeliveryService;<br /><br />import org.w3c.dom.Element;<br /><br />import com.oracle.bpel.client.Locator;<br />import com.oracle.bpel.client.util.WhereCondition;<br /><br /><br />import java.io.IOException;<br /><br />import java.rmi.RemoteException;<br /><br />import java.util.HashMap;<br />import java.util.Hashtable;<br />import java.util.List;<br />import java.util.Map;<br /><br />import javax.naming.Context;<br /><br />import oracle.tip.pc.api.worklist.IWorklistContext;<br />import oracle.tip.pc.api.worklist.IWorklistService;<br />import oracle.tip.pc.api.worklist.IWorklistTask;<br />import oracle.tip.pc.services.hw.task.ITask;<br />import oracle.tip.pc.api.worklist.WorklistServiceException;<br />import oracle.tip.pc.api.worklist.payload.Form;<br />import oracle.tip.pc.api.worklist.payload.PayloadFormGenerator;<br /><br />import oracle.tip.pc.services.hw.task.impl.Task;<br />import oracle.tip.pc.services.hw.worklist.RemoteWorklistServiceClient;<br /><br />import org.w3c.dom.Element;<br /><br /><br />public class DealNegotiatorClient {<br />    public DealNegotiatorClient() {<br />    }<br /><br />    /** see: http://www.huihoo.com/oracle/docs/B14099_19/integrate.1012/b14448/worklist.htm#BHABGEHG<br /> */<br />    public static void main(String[] args) throws ServerException, Exception, <br />                                                  WorklistServiceException {<br />        DealNegotiatorClient dealNegotiatorClient = new DealNegotiatorClient();<br />        // Set JNDI Properties for remote invocation ( will work for local also... <br />        // 1. get a handle to the remove worklist service client<br />        RemoteWorklistServiceClient client = new RemoteWorklistServiceClient();<br /><br />        // 2. set approver's user and password<br />        try {<br />             // note: method client.init() uses the files pc.properties and hw_worklist_jndi.properties<br />             // the latter file specifies the RMI server where the worklist service can be reached as well as the credentials to be used<br />             // the first file sets a number of configuration values for the WorkflowService<br />            client.init();<br />        } catch (WorklistServiceException e) {<br />            e.printStackTrace();<br />        }<br />        String user = &quot;jstein&quot;;<br />        String password = &quot;welcome&quot;;<br />        System.out.println(&quot;Connecting to WorklistService as &quot; + user);<br /><br />        // 3. get worklist context for user<br />        IWorklistContext ctx = null;<br />        try {<br />            ctx = client.authenticateUser(user, password);<br />        } catch (WorklistServiceException e) {<br />            // TODO<br />            e.printStackTrace();<br />        }<br />        System.out.println(&quot;Got Worklist Context&quot;);<br /><br />        // 4. set filters for retrieving My tasks with Assigned status<br />        Map filterMap = new HashMap();<br /><br />        filterMap.put(IWorklistService.FILTER_TYPE_TASK_FILTER, <br />                      IWorklistService.TASK_FILTER_MY);<br />        filterMap.put(IWorklistService.FILTER_TYPE_STATUS_FILTER, <br />                      IWorklistService.STATUS_FILTER_ASSIGNED);<br /><br />        List tasks = <br />            client.getWorklistTasks(ctx, filterMap, IWorklistService.SORT_FIELD_TASK_TITLE, <br />                                    IWorklistService.SORT_ORDER_ASCENDING);<br />        if (tasks != null) {<br />            for (int i = 0; i &lt; tasks.size(); i++) {<br />                Task task = (Task)tasks.get(i);<br />                String taskId = task.getTaskId();<br />                System.out.println(task.getTitle());<br />                <br /><br />                Element payload = (Element)task.getPayload();<br />                float totalPrice = Float.parseFloat((payload.getNodeValue()));<br />                // automatically award another 13% discount<br />                totalPrice = (float)(totalPrice * 0.87);<br />                payload.setNodeValue(Float.toString(totalPrice));<br />                client.updateTask(ctx, task);<br />                // add some comments to the task <br />                client.appendTaskComments(ctx, taskId, <br />                                          &quot;Programmatically manipulated by nl.amis.sales.crm.DealNegotiatorClient on &quot; + <br />                                          new java.util.Date()+  &quot;. New total price set at  &quot; +Float.toString(totalPrice));<br />                                          System.out.println( <br />                                          &quot;Programmatically manipulated by nl.amis.sales.crm.DealNegotiatorClient on &quot; + <br />                                          new java.util.Date()+  &quot;. New total price set at  &quot; +Float.toString(totalPrice));<br />                // close the task by setting its status to DONE<br />                client.customTaskOperation(ctx, taskId, &quot;DONE&quot;);<br />            }<br /><br />        }<br />    }<br />}<br /><br />

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: 

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

 

 

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.

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

2 Comments

  1. Hi,
    I tried to write a simple worklist application with the help of the above sample, but finally I get following error.

    (Note that I’m able to approve the same task by login as jcooper from the sample worklist provided.)

    Exception in thread “main” ORABPEL-10145

    Worklist Service Action Not Permitted Error.
    User “jcooper” is not permitted to perform the action “APPROVE” on task “Hellow” with id “6d2e8a3cd514dfaa:fefe3f:11d86c6cd77:-7ff7″.
    Fix the system configuration if the user does not have privilege to perform the action on the task.

    at com.evermind.server.rmi.RMIConnection.EXCEPTION_ORIGINATES_FROM_THE_REMOTE_SERVER(RMIConnection.java:1621)
    at com.evermind.server.rmi.RMIConnection.invokeMethod(RMIConnection.java:1572)
    at com.evermind.server.rmi.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:55)
    at com.evermind.server.rmi.RecoverableRemoteInvocationHandler.invoke(RecoverableRemoteInvocationHandler.java:22)
    at com.evermind.server.ejb.StatelessSessionRemoteInvocationHandler.invoke(StatelessSessionRemoteInvocationHandler.java:50)
    at __Proxy1.customTaskOperation(Unknown Source)
    at oracle.tip.pc.services.hw.worklist.RemoteWorklistServiceClient.customTaskOperation(RemoteWorklistServiceClient.java:1026)
    at wl2.MyWorklist.main(MyWorklist.java:99)
    Process exited with exit code 1.

    Kindly help me to solve of this.

    Thanks
    Muditha.

  2. Hi, i’m working on a project that has a human workflows and IMHO the worklistAPP is difficult to maintain, my solution was desing and develop a new webAPP using ADF to consume tasks generated in BPEL PM. Can you tell me if the form generated from .task in JDEV can be used in other kinds of WorklistApp ?
    Sorry for my poor english :)