1
votes

I am using Spring Integration with JTA support through Atomikos and JMS bound to different Webshpere MQ and a Oracle Database.
Obviosly for others it seems to be the same thread as in Spring Integration JMS with JTA rollback when message goes to errorChannel but it isn't not at all.
The flow is the following:

  • message-driven-channel-adapter receive the message within transaction
  • some transformations
  • ServiceActivator with deeper business logic
  • DataBase Update

Everything works really fine expect for one szenario:
If an unchecked exception occurs (maybe due to inkonsistent data which shouldn't be) within the ServiceActivator the message is rethrown and handled within an ErrorHandler (via ErrorChannel). There - in some cases - the orgininal incoming message should be send to a DeadLetter Queue (Webshere MQ). This is done with outbound-channel-adapter.

See enclosed configuration:

       <jms:message-driven-channel-adapter id="midxMessageDrivenAdapter"
                                            channel="midxReplyChannel"
                                            acknowledge="auto"
                                            transaction-manager="transactionManager"
                                            connection-factory="connectionFactory"
                                            concurrent-consumers="1"
                                            error-channel="errorChannel"
                                            max-concurrent-consumers="${cmab.integration.midx.max.consumer}"
                                            idle-task-execution-limit="${cmab.integration.midx.idleTaskExecutionLimit}"
                                            receive-timeout="${cmab.integration.midx.receiveTimeout}"
                                            destination="midxReplyQueue"/>
................
<int:service-activator input-channel="midxReplyProcessChannel" ref="processMidxReplyDbWriter" />

        <int:service-activator input-channel="errorChannel" ref="inputErrorHandler" />

        <jms:outbound-channel-adapter id="deadLetterOutboundChannelAdapter"
                                      channel="errorDlChannel" destination="deadLetterQueue"
                                      delivery-persistent="true" connection-factory="nonXAConnectionFactory"/>

Some important hints:
message-driven-channel-adapter:

  • connectionFactory is a MQXAQueueConnectionFactory wihtin an AtomikosConnectionFactoryBean
  • transaction-manager is Spring JtaTransactionManager

outbound-channel-adapter:
connection-factory is a nonXAConnectionFactory.

CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
.....
cachingConnectionFactory.setTargetConnectionFactory(mqQueueConnectionFactory);
return cachingConnectionFactory;

And now the error which I observed:
Although I handled the unchecked exception in my ErrorHandler (ref="inputErrorHandler"; send the message to DeadLetter-Queue) an rollback is initiated and the message-driven-channel-adapter receives the message again and again.
Curious is the fact that indeed the message is delivered to the DeadLetterQueue via the outbound-channel-adapter. The destination (deadLetterQueue) contains the failed messages.

Question: What I'm doing wrong? The failed original incoming message is rolled back although I handled the exception in my ErrorHandler.

Any help really appreciate. Many thanks in advance.

1
Show, please, what your inputErrorHandler doesArtem Bilan
My ErrorInputHandler is a ServiceActivator. It recognize if the incmoing throwable is instanceof MessageTransformationException or MessageHandlingException. In my case (see above) it is a MessageHandlingException. I have to put the code in a new answer because there are too many characters for a comment.bonaly
Do you rethrow exception from your ErrorInputHandler ?Artem Bilan
Definitively no. See my "answer". I couldn't placed all the information within an comment.bonaly

1 Answers

0
votes

concerning to my comment see enclose code of my InputErrorHandler:

if (throwable instanceof MessageHandlingException) {

            MessageHandlingException messageHandlingException = (MessageHandlingException) throwable;

            if (messageHandlingException.getCause() != null
                    && (messageHandlingException.getCause() instanceof CmabProcessDbException
                    || messageHandlingException.getCause() instanceof CmabReplyFormatException)) {

                String appMessage = ((CmabException) messageHandlingException.getCause()).getAppMessagesAsString();

                LOGGER.error(String.format("cmab rollback exception occured: %s", appMessage));
                LOGGER.error("******** initiate rollback *********");

                throw throwable.getCause();

            } else {

                ErrorDto payload = fillMessagePayload(messageHandlingException, throwableClassName);
                sendMessageToTargetQueue(payload);
            }

As I mentioned the "business" ServiceActivator throws an unchecked exception so in this case the ELSE-Statements are calling. Within that I build up a Message with MessagBuilder and sends it ti the errorDlChannel (s. above the outbound-channel-adapter!).

 private void sendMessageToDeadLetterQueue(Message<?> message, String description, String cause) {

.......

        Message<?> mb =
                MessageBuilder.withPayload(message.getPayload())
                        .copyHeaders(message.getHeaders())
                        .setHeaderIfAbsent("senderObject", this.getClass().getName())
                        .setHeaderIfAbsent("errorText", description)
                        .setHeaderIfAbsent("errorCause", errorCause).build();

        errorDlChannel.send(mb);

    }

That's all. This is for this case the last statement. There is nothing anything else in the main method of my ErrorHandler. No rethrow or other stuff.
So this is the reason of my confusion. For me the exception is handled by sending it to the errorDlChannel (outbound-channel-adapter -> DeadLetterQueue).
I saw the message on the DeadLetter Queue but nevertheless a jta rollback occurs...and IMO this shouldn't bee.