Friday, October 07, 2011

Embedding Java in BPEL process


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 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; instance;[] instances; cond;

// Set the whereclause
cond = new "cikey = ?" );
cond.setLong(1, this.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");

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.

No comments: