Tuesday, 19 April 2016

XA Transactions with SOASuite JMS Adapter

JMS is perfect for setting transaction boundaries and in OSB it is pretty clear on how JMS transactions are handled. However, in SOASuite using the JMS adapter the SOA Infrastructure is handling your JMS transactions by default; and messages are removed from the queue rightaway because the Get's are Auto-acknowledged. If something fails, you would expect that messages are rolled back to the JMS queue and eventually moved to the error queue. But, again by default, not with the SOASuite/JMS Adapter. In that case the BPEL process, for instance, fails and get's in a recovery state, to be handled in the 'Error Hospital'in Enterprise Manager. But I want JMS to handle it! (Says the little boy...)

So how do we accomplish that? Today I got the chance to figure that out.

Start with a JMS setup with a JMS Server, Module and a Queue with an Error Queue that is configured to be the error destination on the first queue. On the first queue set a redelivery limit to 3 and a redelivery delay on for instance 60000 ms (or something like that). I'm not going in to that here.
Create also a Connection Factory in the JMS Module with a proper jndi, something like 'jms/myApplicationCF'.

In the JMS adapter on SOASuite there are several OutboundConnectionFactories already pre-configured. It is quite convenient to use the one with JNDI 'eis/wls/Queue'. But if you look into that, you'll see that it uses the default WebLogic JMS Connection factory 'weblogic.jms.XAConnectionFactory'. Not much wrong with that, but you can't configure that for your own particular situation. But more over it is configured with 'AcknowledgeMode' = 'AUTO_ACKNOWLEDGE'. As you can read in the docs there are three values for the AcknowledgeMode:
  • DUPS_OK_ACKNOWLEDGE, for consumers that are not concerned about duplicate messages
  • AUTO_ACKNOWLEDGE, in which the session automatically acknowledges the receipt of a message
  • CLIENT_ACKNOWLEDGE, in which the client acknowledges the message by calling the message's acknowledge method
So create a new outbound connection factory, with a JNDI like 'eis/jms/MyApp'. 
Now, apparently we don't want  'AUTO_ACKNOWLEDGE', because that would cause the message-get acknowledged 'On Get'. So you could rollback until 'Saint Juttemis' (as we say in our family) but it won't go back on the queue. Dups aren't ok with me, so I'll choose 'CLIENT_ACKNOWLEDGE' here. Then there's another option: 'IsTransacted'. I want that one on 'true'. Then in ConnectionFactoryLocation, you'd put the JNDI of your JMS Connection factory, in my example 'jms/myApplicationCF'.

So you'll get something like:

On the tab Transaction, validate that the transaction support is set to a XA Transaction:

Having done that, you can update/redeploy your JMS Adapter with the changed plan. I figure that how to do that is straight forward, especially when you've done that with DB Adapters already.

I created two SOA Projects (actually I adapted those created by a co-worker). The first one is TestPutJMS:

The project is straight forward with a WSDL refering to an xsd with two fields:

  
    
      
      
    
  

The bpel is then as follows:

It assigns the request to the input variable of the invoke of the JMSPut. The JMS_Put is an jms-adapter configuration, referring to the JNDI 'eis/jms/myApp', defined in the JMS Adapter.

After that there's an if on the action field, where in the case of a certain value a fault is thrown, to validate if the Put is rolled back.

In my case it's more interesting to look at the Get part. That project is as follows:

In this case there's a mediator wired to the get adapter config, also referring to the 'eis/jms/myApp' JNDI. The mediator routes to the bpel process. The transaction handling of a mediator is simple and straight-forward:
  • If there's a transaction it will subscribe to that,
  • if there isn't, a new transaction is created.
The JMS Adapter creates an new XA Transaction. On the JMS Adaptor on WLS we configured that no Auto Acknowledge should occur, and we want a transaction. Thus, this is the transaction that is re-used by the Mediator. But how about the BPEL?  The BPEL is asynchronous request only. Since it has no way to reply the response, or it would be on a response queue.
By default you would have a property 'bpel.config.oneWayDeliveryPolicy' set to 'async.persist'. But that would mean that a new thread is started. Setting it on 'sync' would cause the thread that is started by the Adapter is reused. I also want to subscribe to the already running transaction of the JMS Adapter as it is passed through by the mediator. Setting the property 'bpel.config.transaction' to 'required' will take care of that. Summarized, I set the following properties on the bpel:
  • bpel.config.transaction: required => subscribe to already opened transaction
  • bpel.config.oneWayDeliveryPolicy: sync => reuse existing running thread


The process looks like:


Here I have an if with a conditional throw of an exception as well. Based on the value of the action element I can have it to throw a custom exception, that will cause the BPEL to fail and the transaction rolled back.
When I have a redelivery limit to 3, I'll get three retries, so in total 4 tries of the BPEL process. After that, the message is moved to the JMS Error Queue.

A nice article on the JMS Transactions from the A team is found here. However, the setup above leaves the redelivery handling by JMS. So, in 12cR2 that is, I find that the properties of the JMS Queue apparently has preference over the settings I did in the TestJMSGet Service on the composite:

I hope this article clears things up regarding the JMS Adapter configuration for transactions.

7 comments:

  1. Sir,

    bpel.config.transaction: required => subscribe to already opened transaction
    bpel.config.oneWayDeliveryPolicy: sync => reuse existing running thread

    In above lines what do you mean by Thread and A Transaction, can you please eloborate on this.

    If a new thread is created how can I see that difference ?

    ReplyDelete
  2. Hi,

    A thread is a sort of java subprocess used by Weblogic to perform work.
    So also SOASuite does all it's work using threads.
    Reusing the same thread might in this case save you some performance. But if your service is long running then the thread might become STUCK.

    I think you can't really see a difference, unless you come into STUCK-Thread issues.
    Or transactional behaviour that you don't expect.

    Regards,
    Martien

    ReplyDelete
  3. How do we send explicit acknowledge from BPEL after the transaction is over?
    We are trying out this in a small test case and the message does not go away even though transaction is successfully processed.

    ReplyDelete
    Replies
    1. Actually this handled by BPEL. The commit is done at the transaction boundaries. It should do a commit at a dehydration point or at the end of the instance.
      You could ask the question at community.Oracle.Com on the BPEL forum. Then it is easier to elaborate.

      Delete
  4. Hi Martin, great post!

    In your opinion what is the best aproach, treat the errors in the Error Hospital or in a error queue?

    ReplyDelete
  5. Hi Martin, great post!

    In your opinion what is the best aproach, treat the errors in the Error Hospital or in a error queue?

    ReplyDelete
  6. Hi,

    In case of JMS I think an error destination is a good approach since it parks failing messages, frees resources and gives a native means of resubmitting. Even in bulk.

    For many other (non-jms) cases the error hospital is a good alternative.

    I'm reluctant to indicate what the best approach is. It depends on the situation. But working with JMS I by default create an error destination on each destination. In fact, I use scripts that creates an error queue for each queue.

    Regards,
    Martien

    ReplyDelete