Embedding Java in BPEL process

10

Oracle BPEL has a special Activity called "Java Embedding", that allows you to include some "inline" Java code into a BPEL process. Unfortunately, you don’t get Java type-in support in the editor behind the Java Embedding activity, which makes coding a lot more difficult. Furthermore, it is not straightforward to find out what kind of methods are available for you to invoke. When I recently had to use a Java Embedding myself, I found a way to work around this and learned some interesting things about the methods available to you when writing embedded Java code into a BPEL process.....

For starters, to find out where my embedded code ends up at runtime, I put the following, simple statement in a "Java Embedding":

System.out.println("Java Embedding superclass: "+this.getClass().getSuperclass().getName());

This told me that the class that the BPEL compiler creates for this Java embedding extends "com.collaxa.cube.engine.ext.BPELXExecLet", which can be found in orabpel.jar. This is an abstract class, with an abstract "execute()" method, and the code in this method is the code that you provide in the Java Embedding editor. Unfortunately, there is no Javadoc available for this class but there is Javadoc for its superclass (BaseBPELExecLet, click on the link to see the Javadoc).

With this knowledge, it is easy to create a temporary Java class where we can create the code for the Java Embedding activity with full type-in support. My advice would be to create it in a separate JDeveloper project. Add the "BPM Workflow" library to it (Project Properties => Libraries), and create a new Java class that extends the BPELXExecLet class. You’ll need to implement the abstract "execute" method, and here you can create your Java code with full type-in support. When you’re done, you can copy-paste the logic inside the execute method to the editor of the Java Embedding in your BPEL process. There is only one caveat: when your typing code you should not use Alt-Enter to create imports in your Java class; you should always use fully qualified class names or the BPEL compilation process will fail later.

Using type-in support (type this. and wait for a while or press CTRL-space if you are in a hurry, or CTRL-ALT-space if you are in a hurry _and_ want only smart suggestions), a number of interesting methods appear which can be invoked from your code. Some especially useful methods that I have used in the past are:

  • checkpoint(): forces dehydration.
  • setIndex(int i, String s):  stores the value of String s in CI_INDEXES, a table in the dehydration store with the current instance id as PK, and six "index" columns in which you can store data. Typically used to enable you to correlate a unique key for the process in the user domain with the technical BPEL instance ID, for track&trace purposes.
  • getVariableData(): equivalent of bpws:getVariableData() in BPEL process, gives access to any data in the BPEL process
  • setVariableData(): equivalent of bpws:setVariableData() in BPEL process, allows you to change any data in the BPEL process
  • addAuditTrailEntry(): puts a log message in the Audit trail
  • getInstanceId(): gets the current instance id
  • getParentId(): gets the instance id of the BPEL process which invoked the current process
  • getRootId(): gets the instance id of the first BPEL process in the calling chain
  • getPreference(): gives access to descriptor properties.

But this is just a brief summary,  there are many more methods you can invoke. One last method at your disposal that is very powerful is: getLocator(). With the com.oracle.bpel.client.Locator this method returns, you get access to pretty much anything in the BPEL Domain, and one thing you might want to do is to get access to the current BPEL process instance. I ran across this situation where I needed the name of the BPEL process to which the current instance belonged, and although many attributes of the current instance are available through methods in the BPELXExecLet superclass, the ProcessId is not one of them.

The code to obtain a handle to the current BPEL instance using the Locator would look something like this:


      String instanceId = Long.toString(this.getInstanceId());
      // Define variables to use;
      com.oracle.bpel.client.IInstanceHandle instance;
      com.oracle.bpel.client.IInstanceHandle[] instances;
      com.oracle.bpel.client.util.WhereCondition cond;
      
      // Set the whereclause
      cond = new com.oracle.bpel.client.util.WhereCondition"cikey = ?" );    
      cond.setLong(1this.getInstanceId());  
      
      // Perform the query using the Locator
      instances = this.getLocator().listInstances(cond);  
      instance = instances[0];
      
      // Store the name of the BPEL process in the CI_INDEXES table
      setIndex(1, instance.getProcess().getProcessId().toString());

Unfortunately, the code above will fail to find the instance if it has not yet been persisted to the dehydration store. Of course, a call to this.checkpoint();  at the beginning of this code could easily fix that, but this has performance implications.

While trying to find a solution to this problem, I had a feeling that since so much attributes of the current instance _are_ available through superclass methods, it should be possible to obtain the current process instance without performing a query through the Locator. With this in mind, I came across yet another intriguing method in the BPELXExecLet class: getFromEnvironment(String key). Some debugging code later I had found my answer: when using the key "__cube-instance", I could obtain an instance of com.collaxa.cube.engine.core.ICubeInstance, which allowed me access to the ProcessId I needed:


      com.collaxa.cube.engine.core.ICubeInstance instance;  
      instance = (com.collaxa.cube.engine.core.ICubeInstance)getFromEnvironment("__cube-instance");       
      setIndex(1,instance.getProcessId())

Shorter, better performance because 1.) no query needed and 2.) no (additional) dehydration needed, so as long as they don’t change that funky key this’ll do nicely ;-)

This last bit was specific to one particular problem I had to solve recently, but I hope that the first part of this post contains some information that might be of value when you create your own Java Embeddings in a BPEL process.

 

Share.

About Author

10 Comments

  1. This will work:
     

    Element inputElement = (Element)getVariableData(“inputVariable”,”payload”,”/ns2:M6DispatchProcessRequest/oaid”);
    String oaid = inputElement.getTextContent();

  2. Hi Peter
    I’m really new to these things and I request you to provide a sample java application which can be used to query a dehydration store. In my BPEL process I use a human task. And I have a requirment to know if the human task got created, and i have to do this using a java client which will be executed in a remote location. and this java client needs to fetch few parameter to decide the correct task got created.

    Can you please help me?

  3. canwe use getVariableData(Int_Variable) ?

    e.g. int my_Int_var = getVariableData(“BPEL_Integer_Var”);

  4. great post. i have a java embedding step where i parse an incoming string, but when i click on the java embedding step, it just says ” bpelx:exec executed ” i want to see the output value that i am setting in the step. ex. base64ToString, replaceNameSpace, ConvertToXMLStructure. Here the first 2 steps are java embedding and only returns “bpelx:exec”, they store their final values in a variable “xmlString”. i want to see the value of xml string as it passes through the step. any idea how to do it ?

  5. This should work…
    use

    String firstName = ((Element)getVariableData( “inputVariable”, “payload”, “/client:myBPELProcess/client:input”)).getFirstChild().getNodeValue();

    The payload is a text element and the value is for the text element

  6. How do you call getVariableData() if you have to accept a string value from the client and process it further in a Java code.

    I have used the following syntax –
    String firstName = ((Element)getVariableData(
    “inputVariable”, “payload”,
    “/client:myBPELProcess/client:input”)).getNodeValue();
    firstName is always coming out to be a null value even though I pass the input value as string say “test”(after deployment of the BPEL process).

    If I am calling getNodeName() instead then it is correctly giving me the right node as “ns1:input”, but it seems getNodeValue() is having some problems.

    Am I going wrong somewhere?Please guide.

  7. Thanks for sharing this useful information Peter. This uncovers more possibilities with Java embedding than just changing the instance name for example.