Monday, April 21, 2008

Oracle SOA suite - Retrieving process information

The problem statement
To retrieve the information of a process from dehydration store even when I did not have the instance id using data present in the request of the process. (This is a part of a bigger problem I am trying to resolve)

I have an a unique field in the BPEL input. How can I find the BPEL instance and the related instances (This BPEL is executed which in turn executes several other BPEL processes ). I could not modify the existing BPEL so I was left with only one option that was to use the Oracle BPEL Process Manager Client API. This API has very less documentation, most of the help I got was from some blogs specially http://orasoa.blogspot.com/2007/06/calling-bpelesb-webservice-from.html and the API documentation http://download-uk.oracle.com/docs/cd/B31017_01/integrate.1013/b28986/toc.htm.

Driving to the beach
The Marc Kelderman blog solved one of the biggest problems I faced the 'jar hell'. I had tried several other posts but was not successful in setting up the project correctly, but the jar files as specified by him worked wonderfully.

Finally my classpath contained :-
connector15.jar, ejb.jar, oc4j-internal.jar, optic.jar, orabpel.jar, orabpel-ant.jar, orabpel-boot.jar, orabpel-common.jar, orabpel-exts.jar, orabpel-thirdparty.jar, oracle_http_client.jar, orawsdl.jar, xmlparserv2.jar. I am using SOA Suite version 10.1.3.3.

Now I setup a new java project in eclipse with these jars in my classpath. I then setup a server configuration file

Getting your feet wet

## server_config.properties
java.naming.factory.initial=com.evermind.server.rmi.RMIInitialContextFactory
java.naming.provider.url=opmn:ormi://someserver.com:6004:oc4j_soadqa/orabpel
java.naming.security.principal=myname
dedicated.connection=true
java.naming.security.credentials=mypwd
Load the properties:-

 prop = new Properties();
InputStream resourceAsStream = BPELManagerControl.class
.getClassLoader().getResourceAsStream("server_config.properties");
prop.load(resourceAsStream);
The next piece of code I wrote was to retrieve all the instances of a specific BPEL process,



  Locator locator = new Locator("domainname", prop);
WhereCondition whereProcessId = new WhereCondition("process_id = ?");
whereProcessId.setString(1, "myprocessname");
IInstanceHandle[] instanceHandles = locator
.listInstances(whereProcessId);

Taking the dive

And using the instance handles I could display the states and instance id's of the processes. Now this was just the beginning of learning of how to use the API. I needed to modify it to suite my requirements. The previous piece of code returns all record in no particular order, this would make my task very difficult. By trial and error I realized that the API was using a view admin_list_ci to query and all its fields could be used in the query. Thus I added the following to order by creation_date (My instance would be one of the current instances in the server) desc. The next problem was if there was an error the list would continue processing infinitely. So I decided that my instance would be one of the last 50 instance executed on the server. This was a safe assumption since I would be searching immediately after submission. Thus my code became:-


   Locator locator = new Locator("domainname", prop);
WhereCondition whereProcessId = new WhereCondition("process_id = ?");
whereProcessId.setString(1, "myprocessname");
whereProcessId.append("ORDER BY CI_Creation_Date desc");
IInstanceHandle[] instanceHandles = locator
.listInstances(whereProcessId,0,50);
The next problem to resolve was ho do I find if the given IInstanceHandle handle was the instance I was searching for. I needed to search if my application specific id was present in the request of the instance.



  • The IInstanceHandle object had a getField method which seemed to suite my requirements (get the request variable and get the xml from it), but I realized it could only be used for a process that is not finished, thus had to drop the idea of using this method.
  • The only other way to get the data I could find was using the debug and audit xmls. In the BPELConsole along with the flow of the executed instance it also can display the Audit and Debug xmls. Corresponding methods were getAuditTrail and getDebugTrace that gave the dump of the whole BPEL instance data. I decided to use the getAuditTrail as the debug trace referred to the XML as an id (probably a refernce to some other table) which I could not find. Audit trail seemed to be working thus I decided to use it.
   String auditTrailXML = instanceHandle.getAuditTrail();
String XPATH = "//event[@label=\"receiveInput\"]/details/text()";
String receiveInput = Utils.xPathEvaluator(auditTrailXML, XPATH);
XPathFactory factory = XPathFactory.newInstance();
XPath xPath = factory.newXPath();

NamespaceContext ctx = new NamespaceContext() {
public String getNamespaceURI(String prefix) {
String uri;
if (prefix.equals("ns1"))
uri = "http://www.sash.com/Schema/Declaration";
else
uri = null;
return uri;
}

public Iterator getPrefixes(String val) {
return null;
}

public String getPrefix(String uri) {
return null;
}
};

xPath.setNamespaceContext(ctx);
String XPATH2 = "//ns1:appid/text()";
XPathExpression xPathExpression = xPath.compile(XPATH2);
String appid = xPathExpression.evaluate(new InputSource(
new StringReader(receiveInput)));

And from the appid compare with the appid we had and keep on looping until the instance is found. Thus I was successful in retrieving the instance id.

To find all the related instances find the current instance and get it's handle. Then use the following where condition to retrieve related instances:-

    WhereCondition wpi = new WhereCondition("ROOT_ID=?");
wpi.setString(1, instanceHandle.getRootId());

Conclusion

  1. The whole code was mostly based on trial and error and basic API documentation.
  2. The API can be very helpful but very difficult to use
  3. The view admin_list_ci in the BPEL dehydration store can be used to construct the where clause in the query.
  4. Setting up the project is not very simple. The jars have to be correct.
  5. Do you want to use the API? Depends on your requirement. I turned to this API when I had no other option and yes I was satisfied as it resolved my problem.

10 comments:

Olfa said...

Hi
I understood the code but i don't know yet how to build a complete project to retreive information from Bpel processes while the execution(like the current stage in execution or the current filds'values). Please help me ..i'm student and i just started working with bpel processes and Oracle server ...
thanks in advance

Oracle SOA Suite Blog said...

Hi Sashwat,

Can you please let me know using the following code why I am running into the following error?

Code -

The source code of the java class is:-

private void listInstances(){
IInstanceHandle[] instHandles;
IBPELProcessHandle[] procHandles;

IInstanceHandle instance = null;
BPELProcessId bpid = null;

WhereCondition cond;
WhereCondition cond1;
WhereCondition cond2;

try
{
// properties in the classpath
Properties props = new java.util.Properties();

// read the properties file
java.net.URL url = ClassLoader.getSystemResource("context.properties");
props.load(url.openStream());
//System.out.println(props);

cond = WhereConditionHelper.whereInstancesOpen();

// System.out.println(cond.getClause());
// append where clause to search for
// a specific process and version
cond1 = new WhereCondition( "process_id = ?" );
cond1.setString(1, "BPELProcessIndexed");
cond2 = new WhereCondition( "revision_tag = ?" );
cond2.setString(1, "1.0");

// append the whole where clause
cond.append("and").append(cond1).append("and").append(cond2);

// get a connection to the server
Locator locator = new Locator("default","oc4jadmin",props);

//System.out.println(locator.listProcesses());
instHandles = locator.listInstances(cond);
for (int j = 0; j < instHandles.length; j++)
{
// get the instance
instance = instHandles[j];
// get the process for the instance
bpid = instance.getProcess().getProcessId();
System.out.print("Got process " + bpid.getProcessId()
+ "("+ bpid.getRevisionTag() + ") ");
System.out.println(instance.getConversationId() + " "
+ instance.getStatus() + " " + instance.getTitle());
}

}
catch (Exception e)
{
e.printStackTrace();
}

The context.properties file is as follows:-

orabpel.platform=ias_10g
java.naming.factory.initial=oracle.j2ee.rmi.RMIInitialContextFactory
java.naming.provider.url=
opmn:ormi://tcs111250:
6004:
oc4j_home/
orabpel
java.naming.security.principal=oc4jadmin
java.naming.security.credentials=oc4jadmin
dedicated.connection=true

Exception:-

java.lang.Exception: Failed to create "ejb/collaxa/system/FinderBean" bean; exception reported is: "javax.naming.NameNotFoundException: ejb/collaxa/system/FinderBean not found
at com.evermind.server.rmi.RMIClientContext.lookup(RMIClientContext.java:52)
at javax.naming.InitialContext.lookup(InitialContext.java:351)
at com.oracle.bpel.client.util.BeanRegistry.lookupFinderBean(BeanRegistry.java:337)
at com.oracle.bpel.client.Locator.getFinder(Locator.java:956)
at com.oracle.bpel.client.Locator.listProcesses(Locator.java:305)
at com.tcs.poc.bpelclientapi.ListingBPELProcessInstances.listInstances(ListingBPELProcessInstances.java:76)
at com.tcs.poc.bpelclientapi.ListingBPELProcessInstances.main(ListingBPELProcessInstances.java:35)
".
at com.oracle.bpel.client.util.BeanRegistry.lookupFinderBean(BeanRegistry.java:351)
at com.oracle.bpel.client.Locator.getFinder(Locator.java:956)
at com.oracle.bpel.client.Locator.listProcesses(Locator.java:305)
at com.tcs.poc.bpelclientapi.ListingBPELProcessInstances.listInstances(ListingBPELProcessInstances.java:76)
at com.tcs.poc.bpelclientapi.ListingBPELProcessInstances.main(ListingBPELProcessInstances.java:35)
Process exited with exit code 0.

I am using SOA Suite 10.1.3.3 version.
Please advice.

sash said...

1)Start will getting a simple app working. The most difficult thing is to get the classpath right
2) Look at the examples and Oracle documentation, it has enough information to solve this problem
See the view admin_list_ci it has all the info you need. Try to use the API to invoke this and you will have the results.
If u get stuck somewhere I'll try to help

sash said...

>>Oracle SOA Suite Blog
The code seems to be correct please check context.properties for the correct values.

Anonymous said...

I have an issue with the current oracle client api.

The process instances are listed correctly and i can access the instances with the appropriate instance id.

But this only works with finished instances. I am not able to access running instances.

I would appreciate any help :)

MCSE_Karrox said...

I tried both the codes and both seems to work well.

Crsiano said...

hi, good post.
i have same problem
i test your code but he don't func
the compiler don't understand this method.
you create this method in other class??
thanks for atention

Cristiano said...

hi, good post.
i have same problem
i test your code but he don't func
the compiler don't understand this method.
String receiveInput = "Utils.xPathEvaluator(auditTrailXML, XPATH);"
you create this method in other class??
thanks for atention

sash said...

>> Christiano
Create a class Utils and create the following method in it.

public static String xPathEvaluator(String auditTrailXML, String XPATH)
throws XPathExpressionException {
XPathFactory factory = XPathFactory.newInstance();

XPath xPath = factory.newXPath();

XPathExpression xPathExpression = xPath.compile(XPATH);
String decNo = xPathExpression.evaluate(new InputSource(
new StringReader(auditTrailXML)));
return decNo;
}

Cristiano said...

sash
tanks for youre help