Marina
Marina

Reputation: 4064

Tibco +Spring JMS behavior with ErrorHandler on TransactionRolledBackException

I would like to clarify behavior of the Tibco bus (6.1) used via Spring (4.1) JMS config - with and without ErrorHandler specified, and for a specific case of the TransactionRolledBackException.

I configure JMS listener with the "transacted" mode as following:

<jms:listener-container connection-factory="singleConnectionFactory"                 
     acknowledge="transacted" task-executor="myTaskExecutor" concurrency="${my.queue.concurrency}">
     <jms:listener destination="${queue.destination}" ref="myProcessor" method="processMyMessage" />
</jms:listener-container>

and also set 'maxRedelivery' for the queue to 2.

Here are the scenarios of processing an event in the myProcessor.processMyMessage():

  1. message is processed successfully; Result: message is ACK'ed and removed from the queue
  2. a Runtime exception is thrown , either by business logic or by the Spring container (say OOM or HectorException); Result: message is not ACK'ed, put back on the bus and redelivered up to 2 times
  3. org.springframework.jms.TransactionRolledBackException is thrown (by Spring, I assume) - since it is a Runtime exception the resulting behavior is the same as in 2. (is this correct?)

Now, I am adding an explicit ErrorHandler to better log some runtime exceptions:

<jms:listener-container connection-factory="singleConnectionFactory"                 
     acknowledge="transacted" task-executor="myTaskExecutor" concurrency="${my.queue.concurrency}"
error-handler="myErrorHandler">
     <jms:listener destination="${queue.destination}" ref="myProcessor" method="processMyMessage" />
</jms:listener-container>

And the following implementation of the ErrorHandler, where we swallow TransactionRolledBackException:

import org.springframework.jms.TransactionRolledBackException;
import org.springframework.util.ErrorHandler;
public class JMSListenerErrorHandler implements ErrorHandler {
    @Override
    public void handleError(Throwable t) {
        if (t.getCause() instanceof TransactionRolledBackException) {
            log.warn("JMS transaction rollback; reason: " + t.getMessage(), t);
        } else {
            log.error(t.getMessage(), t);
            throw new RuntimeException(t);
        }
    }

What is going to happen for each of the use cases? Here is my guess:

  1. no change
  2. no change - this Runtime exception is re-thrown, so the message will not be ACK'ed and will be redelivered up to 2 time
  3. TransactionRolledBackException: when I handle this exception in the ErrorHandler - is this happening AFTER the TX was already rolled back (as the name RolledBack suggests) and message was marked as not ACK'ed, thus, causing it to be redelivered as in the case 2. ? Or do I effectively cause this messaged to be ACK'ed by swallowing this exception?

Couple more questions:

Thank you,

Marina

Upvotes: 0

Views: 1309

Answers (1)

Gary Russell
Gary Russell

Reputation: 174729

Spring JMS only throws that exception when the underlying JMS client throws a javax.jms.TransactionRolledBackException - it is simply an unchecked wrapper exception - see JmxUtils.convertJmsAccessException().

Take a look at the stack trace and you will see where/why it was thrown.

In general, i.e. if it was thrown on the container's session, catching it won't have any effect - the transaction is already rolled back; but the stack trace is the key.

Upvotes: 1

Related Questions