Powered By Blogger

Search This Blog

Sunday, May 8, 2011

Writing a 'Singleton' BPEL Process

A typical use case in BPEL is connecting to an external resource / system which only support a single connection at a time. In such a case we need to protect against parallel BPEL instances submitting concurrent requests to that resource.

This implies that we need some way to serialize requests for that resource (probably on a first in first out basis). A common design pattern for achieving this is the Singleton, first documented in the Gang of Four's patterns book (where they use a print spooler as an example).

Now BPEL doesn’t explicitly support the notion of a Singleton, however it does allow you to simulate one, which for the purpose of what we are trying to achieve is good enough.

The basic idea is to have an explicit initiateSingleton operation which is used to instantiate a single process instance, and then have a separate invokeSingleton operation which is used to submit each actual request. The invoke operation is then placed in a loop, so as soon as it’s processed one request it loops back round for the next one.

For this example, I’ve just created a simple process which keeps track of the number of times it’s been called. It takes a single parameter, name and returns a string of the following format:

Hello . You are caller number of this singleton.

Pretty simple, but it illustrates the point. The basic process is shown in the screenshot below:


The first activity in the process is a receive activity, InitiateSingleton, which is used to instantiate the process. We then have a simple assign which just sets the count to zero. The remainder of the process, which handles the invokeSingleton operation is contained within the ProcessRequest scope, which itself is placed inside a while loop, that loops for ever. This contains the following activities:
InvokeSingleton – This receives the next request to be processed by the singleton.
Wait – This waits for a period of 5 seconds. It is just to simulate going out to an external system. It also enables us to see how multiple requests for the singleton are queued up by the Oracle BPEL PM Server.
SetResult – Here we just set the result and increment the count by one.
CallbackClient – Finally we just send back the result to the client; before looping back round to process the next request.
Message Correlation
At first glance this all looks deceptively simple, however the complexity comes when you try and call the invokeSingleton operation.

The reason is that when we invoke an operation on a BPEL Process, the message is received by the Oracle BPEL PM Message Handler, depending on the operation it will either invoke a new process to handle it (as with the initiateSingleton operation) or route it through to the appropriate instance of an already running process.

This is where it gets a bit convoluted, when we receive the message for the invokeSingleton operation, even though we only have a single instance of the BPEL process running how does BPEL PM know that it is the correct instance to route the message to? The answer is it doesn’t.

Now under “normal” circumstances, where we have an asynchronous interaction between two processes, Oracle BPEL PM defaults to using WS-Addressing to ensure that messages are successfully routed between two process instances. However in these cases, it’s typical that the interaction between the two processes (say A and B) began by process A initiating process B and passing it details (using WS-Addressing) about itself so that when process B sends back a response it contains sufficient information for it to be routed to the appropriate instance of process A.

However with our Singleton process, any process calling the invokeSingleton operation will have no prior knowledge of that process, so for our purposes we can’t use WS-Addressing to correlate the message.

Fortunately for situations where WS-Addressing isn’t appropriate or available BPEL provides the concept of correlation sets. Essentially correlation sets allow you to use a unique value present in the message body of all exchanged messages (e.g. orderId) to link that exchange of related messages together.

You do this in BPEL by first defining a property which corresponds to the unique value, and then defining a property alias for each message in the exchange, that defines where that property is located within the message. Next you define a correlation set which can consist of one or more properties (see the product documentation or the Oracle SOA Suite Developer's Guide for more details on how to create Correlation Sets).

For our purpose, I’ve defined a simple element called singletonId which is contained in both the initiateSingleton and invokeSingleton operations.

Next we have defined a correlation set SingletonCS which is used in the InitiateSingleton and InvokeSingelton operations.

On the initiateSingleton operation I’ve defined this to initiate the correlation set (see screenshot below), this means whatever value is contained with the singletonId in the start operation must be present in the invokeSingleton operation for it to be routed through to this process instance.


The next part of the problem is to return the response back to the appropriate caller of the process. This at first may seem strange, but recall we may have several processes calling the singleton at the same time, and thus all waiting for a response. We need to ensure that each response is returned to the appropriate instance.

Now you’ve probably already guessed that we need to use correlation sets again (as we have disabled WS-Addressing for this Partner Link). This time the calling process will need to generate a unique key that it passes in the request (requestId) to the singleton. The singleton will then return this in the response to the requester.

If we look at the SingeltonAccessor process, we use the XPath operator generateGUID to generate this value.

Specifying the Reply to Address
So now everything looks fine, so we can go ahead and run the process; well not quite! If you do you will notice that request from the SingeltonAccessor successfully reaches our Singleton process. But for some reason the response from the Singleton never reaches the SingeltonAccessor, in fact if you take a closer examination of the Singleton Audit trail for the callback activity you will see that it actually skipped the callback!

Now it turns out that this is quite a rational behaviour for the simple reason that the Singleton doesn’t actually know where to send its callback to. This is because at design time the caller of an asynchronous process typically isn’t known, and thus the callback address needs to be specified at runt time. In fact if you log into the BPEL Console and examine the WSDL for the call back operation, you will see that the soap:address location for the endpoint is defined as:

http://set.by.caller

Now by default Oracle BPEL PM uses WS-Addressing to handle this, thus when I invoke an asynchronous process, part of the SOAP payload contains a return address for the asynchronous process, however we’ve just disabled WS-Addressing for this message exchange as we are using correlation sets.

So how do we provide the reply to address? Well one way would be to pass this in as part of the request message and then use dynamic partner links to set the replyToAddress.

However a simpler way is to define the replyToAddress property on the partnerlink to point to the reply address. This takes the format:

/partnerLinkTypeName/[rollName]

So in our case:

http://server:port/orabpel/default/SingletonAccessor/1.0/Singleton/SingletonRequester

Now if we deploy this it should finally work. To run the example, first go into the BPEL Console and initiate the Singleton process (set Singleton Id to "Singleton/1.0").

From the console initiate the SingletonAccessor entering whatever value you like for the name. You could kick off a number of these to see what happens.

Loose Ends
So that all works, however there are two basic problems with this approach:
Firstly to invoke the Singleton you need to know the value of the SingletonId. This in our case is fixed within the SingltonAccessor process.
Secondly, the Singleton process will only ever return a result to the SingletonAccessor process (as this is fixed by specifying the replyToAddress). What if we want to call it from another process or web service client?
Actually the solution to this is very straight forward; you use the SingletonAccessor as the entry point for accessing the Singleton. Thus the real client will always call the SingletonAccessor.

Thus the client never needs to know the SingletonId, and secondly as WS-Addressing remains turned on between the “real” client and the SingletonAccesor the client doesn’t need to worry about correlation sets or specifying a replyToAddress.

One final question you may have is how do I prevent multiple instances of the Singleton process being kicked-off by mistake? Well why not try it? You will see that as long as you specify the same value for the SingletonId, BPEL PM will throw a “Conflicting Receive” fault as an equivalent receive activity has already been enabled by the original Singleton.

Conclusion

As we can see, implementing a Singleton within BPEL is relatively straight forward. Whilst this is not a totally clean solution, it does achieve the desired effect.

Click here to download working example

Tuesday, April 5, 2011

Embedding Java in BPEL process

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 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(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"); 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.

Java Embedding

“I have a Customer Entity Bean that allows me to retrieve a SSN based on an email id. How can I invoke that bean from within my BPEL process?”


There are 3 solutions to this problem: • Wrapping the Java component into a full blown web service using JDeveloper or Axis • Using the WSIF Java Binding • Using the Java BPEL exec extension ()


1. BPEL Extension which allows developers to embed a snippet of Java code within a BPEL process 2. When the is reached, the server executes the snippet of Java code (within its JTA transaction context) 3. A set of built-in methods allow developers to read and update scope variables, instance metadata and audit trail 4. XML Façade can be used to simplify DOM manipulation 5. Java exceptions are converted into BPEL faults and bubbled into BPEL process 6. The snippet can propagate its JTA transaction to session and entity beans that it calls


----------------------------------------------------------------------------


BPEL Concepts Built-in Methods Java Embedding Object lookup( String name ) Locator getLocator( ) long getInstanceId( ) String setTitle( String title ) / String getTitle() String setStatus( String status ) / String getStatus() void setIndex( int i, String value ) / String getIndex( int i ) void setPriority( int priority ) / int getPriority() void setCreator( String creator ) / String getCreator() void setCustomKey( String customKey ) / String getCustomKey() void setMetadata( String metadata ) / String getMetadata () String getPreference( String key ) void addAuditTrailEntry(String message, Object detail) void addAuditTrailEntry(Throwable t) File getContentFile( String rPath ) Object getVariableData(String name) throws BPELFault Object getVariableData(String name, String partOrQuery) throws BPELFault Object getVariableData(String name, String part, String query) void setVariableData(String name, Object value) void setVariableData(String name, String part, Object value) void setVariableData(String name, String part, String query, Object value)


What Did We Learn? • There are 3 ways to integrate an existing Java component into a BPEL process: 1) wrapping that component in a SOAP service, 2) using a binding framework such as WSIF or 3) inline a code snippet using • The snippet is executed within the transaction context of the BPEL server. You can propagate that transaction to your own session and entity beans • A set of built-in methods allow the snippet to read and update variables, change instance metadata and throw faults • XML Façade can be used within a snippet to simplify DOM manipulation




Monday, April 4, 2011

Oracle BPEL Fault Policy Framework handling custom business faults

From the Oracle forum and from the comments on my article about Oracle BPEL Fault Policy Management i get a lot of questions about how to let the framework handle my own custom defined ‘business faults’.
In certain situations the default set of faults defined by Oracle aren’t suited enough and you need to define your own faults.
If we look into the examples which got supplied by Oracle we can see an example over here. In this example they defined their own NegativeCredit-fault.

The Oracle BPEL Fault Policy Framework by default only handles the faults which are getting returned on an invoke-activity.
So we have our own custom fault and the knowledge we can only let this fault getting catched by the framework by use of an invoke.
So we need atleast for this testscenario 2 bpel processes. One mainprocess which calls a subprocess, and this subprocess will throw the custom business fault.
This fault will get propogated back to the mainprocess and in here we will be able to let the framework handle the fault.

bpel-invoke

BPEL Processes

Mainprocess

mainprocess

Subprocess
subprocess

Just a simple invoke of the subprocess from the mainprocess. The subprocess with throw a fault, and this fault will be catched in the mainprocess.
throwThe fault to be thrown

From the console start the mainprocess and wait till it comes back with a fault messageinvoke-faultedClick the activity to see the thrown fault

01[2009/02/13 16:24:41]
02"{http://nl.iteye/integration/faults/business}BusinessFault" has been thrown.
03
05<part name="payload">
07 <FaultCode>999FaultCode>
08 <FaultMessage>Something went wrong!FaultMessage>
09 BusinessFault>
10 part>
11BusinessFault>

Ok nice!
So the custom fault we defined in the subprocess reaches the mainprocess.
Now we need to config the fault policy framework so it will get active on our custom business fault.
From the fault we pasted above we need the faultname (BusinessFault) and the namespace (http://nl.iteye/integration/faults/business).

Edit /bpel/domains/default/config/fault-policies/DefaultPolicy.xml and add the next fault :

1<faultName xmlns:flt="http://nl.iteye/integration/faults/business" name="flt:BusinessFault">
2 <condition>
3 <action ref="ora-human-intervention"/>
4 condition>
5faultName>

For testing we will just let this fault getting handled by human-intervention.
Now restart the components

opmnctl stopall
opmnctl startall

Now start the mainprocess again and wait till it fails.

invoke-faulted-frameworkIt looks like the framework got active (activity yellow marked) on our custom business fault.
invoke-faulted-audit

Go to the activities-tab

list-activities
And click the activity which faulted.

edit-activity
Now we can edit some of the values and let the subprocess get re-invoked.

So, at this moment we’re able to throw our custom business faults and let them getting catched by the framework. Since the fault is only getting catched on the invoke of a partnerlink, we aren’t able to let our custom business fault getting throwned to the process in which we maybe want do something with the data for which we actually throwed the custom business fault. So maybe we want to stay in the subprocess and somehow get the custome business fault thrown inhere, let the framework catch it and update the values of this subprocess with new values an re-execute the subprocess.

The next solution will get this done.
The mainprocess won’t get changed but in the subprocess we will invoke a new process called AllBusinessFaults.

New subprocess 2
subprocess2

AllBusinessFaults
all-business-faults

The AllBusinessFaults will throw the custom business fault we ‘request’ back on the invoke in this subprocess. Now it wil get catched by the framework and we will be able to change the values of the subprocess instead of the mainprocess.

list-activities2

By using the AllBusinessFaults bpel service like a sort of composite service, we can add the custom business faults in it and throw the one we would like to get thrown. This will work if the collection of custom business faults isn’t that big. I’m sure there will be better solutions for this, but for the scenario i wanted to describe inhere it was good enough for me.