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:

  1. 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

    ReplyDelete
  2. 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.

    ReplyDelete
  3. 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

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

    ReplyDelete
  5. 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 :)

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

    ReplyDelete
  7. 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

    ReplyDelete
  8. 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

    ReplyDelete
  9. >> 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;
    }

    ReplyDelete
  10. sash
    tanks for youre help

    ReplyDelete