Some 'more, in-depth' information on Oracle BPEL PM, ESB and other SOA, day2day things

Thursday, April 20, 2006

BPEL: Implementing an async callback with the BPEL Java API

In yesterdays note about async miracles, I promised some more info on how to implement a callback with a listener on top of the BPEL java API.

Why would you want to do that? Well, of course you can use the API, to find your instance and then get the result, but this would be blocking again, as long as you are not using a thread - and this is exactly what we will do today.

First here are the steps how you'd do it normally:
  • Step 1: your async process needs a unique ID that you can find it back - the easiest way to this, is to use com.collaxa.cube.util.GUIDGenerator:generateGUID() which returns you a really unique id

  • Step 2: This id must be stored in the NormalizedMessage as property with name conversationId, or you can use the constant (NormalizedMessage.CONVERSATION_ID)

  • Step 3: Initiate your process with the post() api (com.oracle.bpel.client.delivery.IDeliveryService:post())

  • Step 4: due to the async delivery mechanism in BPEL (and worker picks up the message from the delivery queue - it might take some seconds 'til you find the instance) -> introduce some wait time

  • Step 5: Find the instance through it's conversationId, via lookupInstanceByConversationId on the Locator API, which get's you either an instance of IInstanceHandle, or an ORABPEL-02154 -> instance not found

  • Step 6: Check if the instance is closed (completed or faulted) by using IInstanceHandle:getState() and IInstanceHandle.STATE_CLOSED_COMPLETED, or IInstanceHandle.STATE_CLOSED_FAULTED

  • Last step is to retrieve the variable defined in you BPEL flow by using getField(String fieldName):java.util.Hashtable(). Why a hashtable? Because you can have multiple parts and each part is an entry here, with the key being the name of the part



Ok - back to the real callback implementation:

  • First we need to define some kind of callback interface, I decided to go with 2 methods, that a developer needs to implement

    • public void onResult (Map pResultMessageParts); which is called when the process has finished, and

    • public String getVariableName (); which is called by our thread to determine the field we need to return to the above content


    An implementation of this callback would be passed later on to the thread, and would be used to signal the callback.

  • Now it's time to dive into the BPEL interfaces, and get an idea of what information you'd need to query for an instance, and where to get it from

    • To logon to the domain you need a DomainAuth object (which represents a "BPEL" session) - this can be retrieved from the Locator:getDomainAuth()

    • A conversationId to find your process instance back

    • and the field name to examine the data, all from the interface described a step above



  • Next step was to implement a Thread (say be extending java.lang.Thread, that would query the BPEL Server for getting the instance, and later its state)

  • All you need to do next is add a constructor, that takes these parameters, and add some logic to the run() method - with a condition when the thread should stop. I chose to break after I triggered the callback's onResult method

  • The run() method does the following

    • has an initial sleep, to minimize the chance of an instance not found error

    • uses the Locator to findthe instance (lookupInstanceByConversationId)

    • asks the instance for it's state - if completed it (IInstanceHandle:getState())

    • retrieves the field (IInstanceHandle:getField(String))

    • and calls the onResult method on the passed callback instance. When this happens it breaks the loop



All you need to do now is to initiate the thread from your client program, pass the parameters, and start it :-D

Happy threading ..

Having questions? send your feedback on Implementing an async callback with the BPEL java api here

3 Comments:

Anonymous Parag J. Kansara said...

Most important thing to remember (as mentioned by Clemen in Step 1 and Step 2) is to assign a "Unique Id". Though the documentation talks about this too, there is no constraint on the table holding Process information. The BPEL process will run and create a record with the same Conversation Id if same id is supplied again (say for ex. "PJK061"). Everything works fine. The real problem comes up when you try to retrieve state of an instance (Step 6). Now you will get

ORABPEL-02154
Multiple instances found.
Multiple instances were found with the key "PJK061". Only one instance should be returned per key.

How about adding constraint on the table OR make BPEL PM smart enough to find if there is already an instance with CONVERSATION_ID "PJK061" ?

- Parag J. Kansara
(DoIT - University of Wisconsin)

11:35 PM

 
Blogger Clemens Utschig - Utschig said...

that's why you need to be carefull with exception handling, and in case you get the famous orabpel-2156 you need to go back to the start (preferrably within a loop or so)..

In 10.1.3.1. there is a sample - and a watchdog that does this for you :D

in $SOA_HOME/bpel\samples\tutorials\102.InvokingProcesses\rmi\com\otn\samples\async

hth clemens

10:42 PM

 
Anonymous Anonymous said...

Where I can get a com.oracle.bpel.client.Locator.class??

2:47 PM

 

Post a Comment

<< Home