Recently, I was working on an Forms application that required a Form to retrieve and complete a Human Workflow task from the worklist of a BPEL 10.1.3 server (also see this post, where you’ll see how Java code very similar to the code that I will provide here can be embedded in Oracle Forms). Although it wasn’t all that difficult, it did take some time getting everything configured correctly, and judging from some of the forum posts out there I was not the only one to struggle with it. This post will take you through the setup of a new JDeveloper project and the creation of a simple POJO that will be able to query and manipulate the worklist of a remote BPM server.
Environment Requirements
I have tested the steps in this blog post using a SOA Suite 10.1.3.1 installation, and JDeveloper 10.1.3.1. Although the process will probably be pretty similar in older versions, I cannot vouch for this..
Project Setup
As usual, the most difficult part isn’t the Java code but setting up the classpath and the environment description files. Especially since the only error message you seem to get when _anything_ is wrong is:
ORABPEL-30028 Invalid configuration file wf_config.xml The configuration file wf_config.xml not be read. Make sure that the configuration file wf_config.xml is available and is a valid XML document. Contact oracle support if error is not fixable.
That sure sounds like the ticket to solving all your problems is a file called wf_config.xml, isn’t it? Well, maybe in some cases, but as we will see this file isn’t even part of the solution (at least not the one used here). But we’re getting ahead of things. Lets first create the JDeveloper Application (make sure the Application Template is “No Template [All Technologies]“):
And then the project:
Next thing is to set the classpath/libraries on the newly created Project. You can do that by double-clicking the newly created Project and choosing the “Libraries” page.
You can get all the necessary code to compile using the few standard JDeveloper libraries shown below (add them using the “Add Library” button), but unfortunately you can not get it to run correctly. Apparently, it is expected that you deploy all your code to the BPM server, but in my case the Java code was running on a different application server. Therefore, in addition to the standard libraries shown below, I had to add the orabpel jars to the classpath as well. You can locate these jars in the bpel/bin directory below the OracleAS root directory. You can copy these jar files to your project directory and add them using the “Add Jar/Directory” button.
The final classpath of the project therefore looks like this:
Note: The library “Oracle9iAS” has a confusing name, it actually points to the embedded OC4J of JDeveloper 10.1.3.1 which is, of course, an OC4J 10.1.3.1.
Now before we can start writing code, there’s one more thing we need to do. When connecting remotely to the BPM Server (SOA Suite 10.1.3.1 in my case), you need a file called “wf_client_config.xml” on your classpath. You can copy this file from the /bpel/system/services/config directory below the OracleAS root directory. Place the file in the src directory of your JDeveloper project (create it if it isn’t there), and hit the refresh button to have it shown in JDeveloper. In all likeliness, the only thing you’ll need to change in the content of the file is the first part, where you should use the commented as we are dealing with an OPMN managed instance rather than a standalone OC4J:
<servicesClientConfigurations xmlns="http://xmlns.oracle.com/bpel/services/client"> <ejb> <!--serverURL>ormi://soaserver:home/hw_services</serverURL --> <!-- for stand alone --> <serverURL>opmn:ormi://soaserver/hw_services</serverURL> <!-- for opmn managed instance --> <user>oc4jadmin</user> <password>secret</password> <initialContextFactory>oracle.j2ee.rmi.RMIInitialContextFactory</initialContextFactory> </ejb>
If for some reason you run into problems connecting to the SOA Suite later on (which problably manifests itself as beforementioned message “ORABPEL-30028 Invalid configuration file wf_config.xml “), chances are the solution is in the content of this file. Also be aware of the fact that the OPMN is very particular about the IP address on which it is invoked; if it is started with primary IP address xxx.xxx.xxx.xxx, it won’t respond if the DNS resolves its name (soaserver in this case) to yyy.yyy.yyy.yyy, even though this might be a perfectly valid address for the machine on which the OPMN is running!
The Code
Now that the project has been set up, you’ll find that the actual code with which you can access the Worklist is actually fairly straightforward. As an example, we’ll create a simple service-like class:
// Change these constants to match your setupprivate static final String WF_MANANAGER_UN = “oc4jadmin”; private static final String WF_MANANAGER_PW = “welcome1”; private static final String LDAP_DOMAIN = “jazn.com”; String worklistUser; public WorklistService(String userName) |
To this class, we’ll add a few methods one by one that will explain the use of the Worklist API.
Connecting to the Workflow Service
The “main point of entry” for all worklist-related activities is the IWorkflowServiceClient. This is actually an interface for which a couple of implementations exist, which can be obtained through a factory:
public IWorkflowServiceClient getWorkflowServiceClient() |
The REMOTE_CLIENT constant that is passed into the factory method returns a WorkflowServiceClient that is able to correspond with a remote SOA Suite installation. Other “flavours” are JAVA_CLIENT, LOCAL_CLIENT, WSIF_CLIENT and SOAP_CLIENT, with which I do not have much experience at this time.
With an instance of IWorkflowServiceClient, we can now get access to a number of more specialized Service objects. We’ll start with a Service that allows us to perform queries against the worklist, the ITaskQueryService:
public ITaskQueryService getTaskQueryService() |
On this specialized ITaskQueryService object, we can invoke several interesting methods, such as queryTasks(), getTaskDetailsById() etc. All these methods, however, have a required parameter of type IWorkflowContext. Simply put, this means that you can not invoke these methods unless you are “logged in” to the worklist of a specific user. Therefore, we’ll need the following method first:
public IWorkflowContext getWorkflowContext() throws Exception |
The authenticate method takes 4 String arguments, the last of which is optional (that is, could be null). They are: username, password, ldap domain and “on-behalf-of-user”. If your code has access to the username and password of the Workflow user of which you want to query the worklist, you only need to supply values for the first three arguments and leave the fourth null. However, if, as is very likely, you know the username but NOT the password of this user, you can use the fourth argument to create a IWorkflowContext on behalf of this user, while logging in as the OC4J administrator. In this example we use the latter approach.
Querying the Worklist
Well, we have the “low level plumbing” in place, so lets get to the more interesting stuff: perfoming a query against the Worklist:
// The Predicate class is used to construct a “whereclause” for querying the worklist.
Predicate whereclause = new Predicate(TableConstants.WFTASK_STATE_COLUMN, // Using the addClause method, you can create much more complex whereclauses. // set list of columns / optional info List tasks = getTaskQueryService().queryTasks(getWorkflowContext(), displayColumns, optionalInfo if (tasks != null) =”#000000″>; |
There are a couple interesting things to notice here. First is the use of the Predicate class to effectively construct a whereclause with which to query the worklist. Notice in the commented code an example of how arbitrarily complex whereclauses can be constructed using the addClause() method, using Predicate.AND and Predicate.OR and a large number of logical operators such as OP_EQUALS, OP_CONTAINS, OP_IS_NOT_NULL, OP_GTE etcetera.
Second is the bit where a List of display columns is constructed. Here you can see that the API of the ITaskQueryService is created with the typical needs of a worklist application in mind. Each individual Task contains quite a lot of data, and then there is the custom “task payload” that could also be of considerable size. It would be very inefficient if ALL the data for EACH Task in the result set has to be queried into memory, just to show a few fields in a worklist table. Therefore, the Tasks that are returned by the queryTasks() method are not “full blown” Tasks, but rather like placeholders, surrogates. Although they implement the Task interface, all methods will return null except for the “display columns” that are provided to the method as a List. In the code above, I have chosen to query the creation date, title and identification key attributes. Unfortunately, I have not found a constants class for these “display columns”, so I had to make educated guesses. If anyone knows where these constants can be found, a comment on this blog would be greatly appreciated!
Finally, you might wonder about the return type: a String array where each String is constructed by invoking method retrieveTaskData. This was the most convenient signature for embedding this Java code in an Oracle Forms application, see this post for further details.
Completing Tasks
Now that we have retrieved Task information from the worklist, let’s add a few methods to demonstrate how Tasks that have just been queried can be completed programmatically. First we’ll need a new Service object because the ITaskQueryService does not (what’s in a name) provide us with any methods that allow us to make changes to Tasks. For that, we’ll need an instance of ITaskService:
private ITaskService getTaskService() |
The last method of this post will demonstrate how to complete a Task.
public void completeTask(String taskId, String outcome) throws Throwable |
The thing to notice here is that because the Tasks that were returned earlier by the queryTasks() method are “surrogates” and not “the real thing”, we will first need to retrieve the full Task object. For this we use the getTaskById() method. With this Task, we can invoke the updateTaskOutcome() method of the TaskService.
Interesting detail here is that, had we made any changes to the Task here, these changes would have been persisted as well. In a future post, I’ll show how to manipulate the “Task payload” programmatically.
Finally, and just for completeness’ sake, I’ll provide a main method to test the methods listed above (as if you couldn’t come up with this yourself ;-D).
public static void main(String[] args) |
Conclusion
Creating a remote connection to a BPEL PM server to programmatically manipulate the Tasks in the Worklist requires little and rather trivial coding. The trickiest part is getting the compile-time and runtime classpath right, and getting the all-important wf-client-config.xml file in place. From there, the possibilities are endless. For instance, check this blog entry!
Do you have a working version of this code for SOA Suite 11g PS3. This is our requirement and it would be wonderful to have it.
Here’s the error I see. Any help is appreciated!
Finding tasks for user weblogic
Obtaining TaskQueryService
Attempting to create WorkflowServiceClient
Created WorkflowServiceClient
Creating WorkflowContext
oracle.bpel.services.workflow.client.WorkflowServiceClientException: javax.naming.NameNotFoundException: Unable to resolve ‘TaskQueryService’. Resolved ” [Root exception is javax.naming.NameNotFoundException: Unable to resolve ‘TaskQueryService’. Resolved ”]; remaining name ‘TaskQueryService’
at oracle.bpel.services.workflow.query.client.TaskQueryServiceRemoteClient.authenticate(TaskQueryServiceRemoteClient.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at oracle.bpel.services.workflow.client.WFClientRetryInvocationHandler.invokeTarget(WFClientRetryInvocationHandler.java:121)
at oracle.bpel.services.workflow.client.WFClientRetryInvocationHandler.invoke(WFClientRetryInvocationHandler.java:67)
at $Proxy13.authenticate(Unknown Source)
at qpim.bpel.WFTest.getWorkflowContext(WFTest.java:95)
at qpim.bpel.WFTest.getTasksForUser(WFTest.java:136)
at qpim.bpel.WFTest.main(WFTest.java:199)
I got the same error message as mentıoned by Alexandre(October 9th, 2007 at 4:58 pm ) :
javax.naming.NameNotFoundException: TaskQueryService not found
Telman seems to have resolved it, I admin I did not understand the solution. Please help.
Thanks
Hi Thanks for this blog.It has helped me a lot..
Keep up the good work
Some general questions….
1. What experience is there that the Worklist manger is an effective application. Would you recommend this solution.
2. The APEX integration.. has this been very successful.
3. We are a large user of APEX. Can the worklist manger call an APEX form and have the task actions wrapped around the APEX form(s).
I have updated /etc/hosts as
soaserver_ip domain domain.telman.com
and the javax.naming.NameNotFoundException: TaskQueryService not found problem has been solved.
Actually you pointed to it at the begining of this article. but ı have missed it.
Thanks for this excellent article.
Regards,
Telman
I got the same error message as mentıoned by Alexandre(October 9th, 2007 at 4:58 pm ) :
javax.naming.NameNotFoundException: TaskQueryService not found
Could you please advise. any suggestıon will be appreciated
Regards
Telman
Amit, thanks for taking the time to share this! At the time when I wrote this, and with the versions of the software I used for this article, it ran successfully, but if people trying this now with the current versions run into similar issues, this will be a great help! Perhaps you could provide some indication of these issues you mentioned (error messages perhaps?), to help people find your solution through Google!
Thanks again,
Peter
Thanks for this excellent article. It jumpstarted me to use java to access the worklist.
I ran into issues when trying to run. Finally I made sure that I had the following jars in the libraries (and nothing else…so I had to remove BPM Workflow, Oracle 9 iAS etc, as mentioned in this post)
$SOA_HOME/bpel/lib/bpm-infra.jar
$SOA_HOME/bpel/lib/orabpel-common.jar
$SOA_HOME/bpel/lib/orabpel-thirdparty.jar
$SOA_HOME/bpel/lib/orabpel.jar
$SOA_HOME/j2ee/home/jazncore.jar
$SOA_HOME/j2ee/home/oc4jclient.jar
$SOA_HOME/lib/xml.jar
$SOA_HOME/lib/xmlparserv2.jar
$SOA_HOME/webservices/lib/orasaaj.jar
$SOA_HOME/webservices/lib/soap.jar
$SOA_HOME/bpel/system/services/lib/bpm-services.jar
$SOA_HOME/bpel/system/services/config
$SOA_HOME/bpel/system/services/schema
wsclient_extended.jar (download from http://download.oracle.com/otn/java/oc4j/1013/wsclient_extended.zip)
I got this list from http://download-west.oracle.com/docs/cd/B31017_01/integrate.1013/b28981/worklist.htm#sthref2664
The following forum post was useful too
http://forums.oracle.com/forums/thread.jspa?threadID=528642&start=0&tstart=0
Hi Peter,
One thing I want to Know about this WorkList ,I.e., Can we Assign Some Value for groupTaskId I mean Can we Use the groupTask for Verifications like assigning some constant to it.Because we are getting an at oracle.oc4j.rmi.OracleRemoteException when we assign some value to it.
Thanks
Uday
Kindly tell me How do I aquire the task before “APPROVE”
Murali, like I wrote in reply 21: it is the bottom of the stacktraces that is the best place to look for the source of the problem, not the top part that always says “Invalid configuration file wf_config.xml”. Also, could you indicate after which statement your problems occur?
Hi,
i am trying to fetch task list of a particular by using the same way, but i am unable to get connection of work list. its showing the following error message. ORABPEL-30028
Invalid configuration file wf_config.xml
I have configured user name and pwd according to my server. do i need to specify any server name in my java code. we are not specifying server name any ware because my server is in different machine, if it is like this how it will connect to work list.
Where do we need to give server details. and what do i need to give realm name because my server is configured with OId. i am using SOA suite 10.1.3.3.
I have used above code in this article. other than this i have n’t changed any configuration. following is my wf_client_config. xml configurations:
opmn:ormi://soa.sahaj.com:home/hw_services
oc4jadmin
something
oracle.j2ee.rmi.RMIInitialContextFactory
Please help me to resolve this.what do i need to do to resolve this.
Hi Thirumal, like I stated in the article you will get this error if _anything_ goes wrong. You get it when the dehydration store database is offline, or when the Task you are about to complete can’t be completed by the current user, or whatever else might be going wrong. Typically, the long stacktraces that accompany this error will provide some more information (the more to the bottom you get, the closer you’ll get to the real source of the problem).
hi
i am getting the
ORABPEL-30028 Invalid configuration file wf_config.xml
The configuration file wf_config.xml not be read
error when i include this : worker.completeTask(s,”APPROVE”). if i comment this piece of code the program executes and returns me the task for a given user. can you explain more on including wf_client_config.xml in classpath and getting the task’s payload.
Thanks
hi
i am getting the following error Invalid configuration file wf_config.xml ORABPEL-30028
also can you pls explain more on congiguring and including “wf_client_config.xml” in classpath
thanks
About the task payload: sorry, I have not read carefully what you wrote about the attributes. Using TASKPAYLOAD will return the payload as well. Or I could use getTaskQueryService().getTaskDetailsById(getWorkflowContext(), id); to get all the task’s attributes. Regards!
Thanks for the article, it is working. However I have a problem getting the task’s payload. In the retrieveTaskData method t.getPayload() always return null, but I can see the payload in BPEL Console and also in the BPEL Worklist application. Any idea, what am I doing wrong? I am using SOA suite 10.1.3.1 on Linux.
I tried to run your sample, but a got a “javax.naming.NameNotFoundException: TaskQueryService not found” at line workflowContext = getTaskQueryService().authenticate(WF_MANANAGER_UN, WF_MANANAGER_PW, LDAP_DOMAIN, worklistUser);
I have no idea about what can be wrong.
Hi Bharanidhar,
BPM authorization service worked with Java Client for me too. I figured it out from the below URL that Identity Service is not available for Remote Client.
Check the content “Table 16-6 Clients Available for the Workflow Services” from the URL http://download.oracle.com/docs/cd/B31017_01/integrate.1013/b28981/worklist.htm#BHACHGBE
If you still want to use this service with the Remote Client, try creating a WebService for the methods in BPM Authorization service. Please post if you find any other alternative solution.
Hi Peter,
Thanks for this blog entry !
It worked perfectly.
Benoît
I figured it out on how to deploy to app server. i had to change to WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.JAVA_CLIENT) from WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT)
Hi Rama,
I tried using BPMAuthorizationServiceFactory. but no success so far.I am planning to use oracle’s OID Java API’s to retrieve the user information. did u taste success in deploying the application built using the Worklist API mentioned in this blog? Everything is working fine from Embedded OC4J of jdeveloper. But when i deploy it to the application server (part of SOA suite) it fails. Any pointers would be very helpful.
Hi Bharanidhar,
I am facing the same issue with BPMAuthorizationService when using with REMOTE_CLIENT. I am getting null object. Did you get any alternate solution. Please post the solution if you find any.
Thanks.
Hi Peter,
I couldn’t get BPMAuthorizationService object value. It always throws me null value. Please see the code below
———-
WorklistService worker = new WorklistService(“jcooper”);
IWorkflowContext context = worker.getWorkflowContext();
ITaskQueryService queryService = worker.getTaskQueryService();
IWorkflowServiceClient client = worker.getWorkflowServiceClient();
BPMAuthorizationService bpmAuthServ = client.getAuthorizationService(“jazn.com”);
—–
i require BPMAuthorizationService inorder to retrieve BPMUser value for each task’s ShortHistoryTaskType something like the following
—-
BPMUser bpmUser = bpmAuthServ.lookupUser(individualShortHistoryTask.getUpdatedBy().getId());
String firstName=bpmUser.getFirstName();
String surName=bpmUser.getLastName();
Is there any alternative solution for this?
Hi Peter,
Thanks for your reply. I did change the static final constant LDAP_DOMAIN in the WorklistService class.
After a long struggle, i fixed it by making the following changes in wf_client_config.xml
opmn:ormi://hostname:6004:oc4j_soa/hw_services
oc4jadmin
bpel123
oracle.j2ee.rmi.RMIInitialContextFactory
Bharanidhar, I trust you also changed the value of the static final constant LDAP_DOMAIN in the WorklistService class before deploying?
Hi,
Very helpful article. Kudos to you.
I have a BPEL server running in windows machine. I followed the steps mentioned in this article meticulously and was able to create a wokflowContext and i could get all the tasks of a particular user in default realm, jazn.com.
But when i tried deploying the same in SOA Suite 10.1.3.1 on SOLARIS SPARC with OID configured with a different realm name, i am not successful in creating the workflowContext. I just edited the realm information in wf_client_config.xml.
Is there any more XML files i need to tweak into? Did any of you encountered a similar situation?
Good article – now I’m awaiting desperately the announced follow-up on how to programmatically manipulate the task payload.
Hello Peter,
great article! I have been searching desperatly for a solution for this problem for quite a while! Thanks a lot! Cheers Patrick
I have a problem creating a Web Service to access de Worklist , somebody knows how to do that
Looks like the constants for “display columns” are in oracle.tip.pc.api.worklist.WorklistColumns as of 10.1.3.1
Hi, thanks for the info, it was very helpful. But I cannot complete the task. The error is :
ORABPEL-30036
Invalid action on workflow task.
Action OUTCOME_UPDATE on task eb2f8e15e90f5c0c:-104b5f91:11234630d79:-7b26 is not valid because of The user has not acquired the task..
Contact oracle support if error is not fixable.
And when I acquire the task first and complete the task using the same user, the error is :
ORABPEL-30036
Invalid action on workflow task.
Action OUTCOME_UPDATE on task eb2f8e15e90f5c0c:-104b5f91:11234630d79:-7fe3 is not valid because of The task has been acquired by some other user..
Contact oracle support if error is not fixable.
Hi, thanks for the information, it was very helpful.
You can find the constants for the "display columns" at oracle.bpel.services.workflow.repos.table.WFTaskConstants.
I have some problems using the remote API to inititate bpel process, using the Locator and the IDeliveryService. Do you have any examples about this?
Thanks a lot.