Archive for November, 2011

Global transactions with JMS or AQ and BPEL in 11g

The concept of a global (XA) transaction is very useful in many integration scenarios, as it is much easier to operate a solution where all changes or no changes are performed. BPEL is primarily intended for long-running processes where a single global transaction is impractical, but even so it is often possible to model using transactional parts with known milestones. This pattern is used in Oracle AIA, for example. In the real world many organizations use BPEL for simple processes where a single global transaction can be used as well.

Assume that a message arrives on a queue. It needs to be transformed and processed somehow; then another message should be written to an output queue, a database should be updated and a long-running asynchronous process spawned. If something goes wrong the message should be rolled back to the input queue where it can be retried (or eventually moved to dead letters by the queue manager) and all the other changes should be rolled back as well.

This was possible to do in, but it required some black magic. The WSDL file for the input adapter had to be modified and all the BPEL processes had to run in-memory with completionPersistLevel=off and with transaction=participate everywhere. It worked, but the instances would never appear in the BPEL console, making it hard to see what was going on in the system.

With 11g this works much better. It is no longer necessary to use in-memory optmization and the full audit trail can be saved and viewed in the console. The Mediator can be used as well. However, the procedure is still poorly documented and it still requires manual changes in the generated code.

To try it out, create two queues (one inbound and one outbound) and a test table. Create a new composite. Add an inbound AQ or JMS adapter and an outbound AQ or JMS adapter. Add a database adapter for the test table. Create an asynchronous BPEL process that does nothing. Create a synchronous BPEL process that inserts a record into the table, calls the asynhrounous BPEL process and finally enqueues a message into the output queue. Finally create another BPEL process or Mediator component that dequeues a message from the input queue and invokes the synchronous BPEL process.

Enqueue a message on the input queue. The entire flow should appear in the console, a new record should be present in the table and a message should appear in the output queue. So far so good. Now, disable enqueue for the output queue and try again. What happens? Without a global transaction the message will be removed from the input queue, a new record will appear in the table, the asynchronous process will run but nothing appears on the output queue!

Right, do the obvious thing. Edit composite.xml and add bpel.config.transaction=required for all the BPEL processes. Assuming that the application server has been configured correctly with an XA data source this should do the trick, right? Wrong. The changes are rolled back, so there will be no new record in the table and no asynchronous process. However, the message is still removed from the input queue and lost.

Time for some undocumented magic. Edit the WSDL file for the input adapter and add a new type, a new element and a new message. Include the new message as output message, making the WSDL synchronous. For example (assuming an opaque JMS adapter and showing only the relevant parts):

Edit the BPEL process or Mediator component that dequeues the message and add a reply to the adapter. Good to go? Almost. Everything looks good, but for some reason the message will still be lost. As a final step the input adapter must be configured with jca.retry.all=true in composite.xml. With that option in place, deploy the code and try again. This time the message should be returned to the input queue and retried and eventually moved to dead letters, where it can be handled.

In summary:

1) Configure all BPEL processes with bpel.config.transaction=required in composite.xml.
2) Modify the generated WSDL file for the AQ or JMS adapter and add an output message, making it synchronous.
3) Configure the AQ or JMS adapter with jca.retry.all=true in composite.xml.
4) Use an XA data source.

This has been tested with

Categories: SOA Suite