What happens on a JMS queue when onMessage() throws a JMSException? - spring

I'm using Spring 2.5 with my custom class that implements MessageListener. If a JmsException is thrown in my onMessage( ) method, what happens to the state of the queue?
Is the message considered "delivered" by the queue the moment onMessage is called? Or does the JmsException trigger some kind of rollback and the message is re-entered on the queue?
Thanks in advance!

From the JMS 1.1 spec...
4.5.2 Asynchronous Delivery
A client can register an object that implements the JMS MessageListener interface with a MessageConsumer. As messages arrive for the consumer, the provider delivers them by calling the listener’s onMessage method.
It is possible for a listener to throw a RuntimeException; however, this is considered a client programming error. Well-behaved listeners should catch such exceptions and attempt to divert messages causing them to some form of application-specific ‘unprocessable message’ destination.
The result of a listener throwing a RuntimeException depends on the session’s acknowledgment mode.
AUTO_ACKNOWLEDGE or
DUPS_OK_ACKNOWLEDGE - the message
will be immediately redelivered. The
number of times a JMS provider will
redeliver the same message before
giving up is provider-dependent. The
JMSRedelivered message header field
will be set for a message redelivered
under these circumstances.
CLIENT_ACKNOWLEDGE - the next message
for the listener is delivered. If a
client wishes to have the previous
unacknowledged message redelivered,
it must manually recover the session.
Transacted Session - the next message
for the listener is delivered. The
client can either commit or roll back
the session (in other words, a
RuntimeException does not
automatically rollback the session).
JMS providers should flag clients with message listeners that are throwing
RuntimeExceptions as possibly malfunctioning.

Related

Throwing exception outside of Flux/Mono reactive pipeline

In my spring JMS listener onMessage method, I am using a reactive pipeline as as below where I receive a JMS message from a queue and transform that to a different format and publishing to another topic. Without the reactive flow, in the case of failure in output publishing, I can throw a runtimeexception for the same message to redeliver from the queue. But by using the below flow of reactive approach, I cannot find a way to throw an exception outside. Can you please help on what should be the approach here for throwing the exception outside to get the message redelivered
public void onMessage(Message message) {
Mono.just(message)
.doOnNext(LoggingUtil::logMessage)
.flatMapIterable(messageTransformer::transformToTriggerEvents)
.doOnNext(LoggingUtil::logTriggerEvent)
.doOnNext(triggerEventPublisher::publish)
.subscribe();
}

How to handle Connection Error in Message Driven Beans?

I have a Message Driven Bean listening to MQ Receiver Queue for incoming Messages.
My MDB is implementing MessageListener and overriding OnMessage method, where I can handle any exception related to the received messages.
All the Connection & Queue parameters are in ejb-jar.xml and weblogic-ejb-jar.xml.
So in case of any Connection failure (e.g. MQ is down, I just get Exception in the console).
How should I catch and handle these connection failure related exceptions in MDB? As I have requirement to send Email notifications to Support group in case of such Connection Failures.
I just saw some info about javax.jms.ExceptionListener, but there is no way I could use it for my setup as I don't have direct access to my connectionfactory / connection objects in case of my MDB.

What's the correct exception type to NACK a message using annotation based listener in spring-boot with amqp?

I'm using spring boot with spring-amqp and annotation based listener to consume message from a rabbitmq broker.
I've a spring component which contains a method like this:
#RabbitListener(queues = "tasks")
public void receiveMessage(#Payload Task task) {...}
I'm using the AUTO mode to acknowledge messages after successful execution of receiveMessage(...). If i detect a special error, i'm throwing AmqpRejectAndDontRequeueException to get this message into a configured dead letter queue. Now i need to nack a message only, so that the message gets requeued into the main queue of rabbitmq and another consumer has the possibility to work on that message again.
Which exception should i throw for that? I wouldn't like to use channel.basicNack(...) like described here (http://docs.spring.io/spring-integration/reference/html/amqp.html) if possible.
As long as defaultRequeueRejected is true (the default) in the container factory, throwing any exception other than AmqpRejectAndDontRequeueException will cause the message to be rejected and requeued.
The exception must not have a AmqpRejectAndDontRequeueException in its cause chain (the container traverses the causes to ensure there is no such exception).

Difference between AUTO_ACKNOWLEDGEMENT mode with and without Spring JMS

I am trying to understand how acknowledgement modes work in JMS. I was reading this source and it hugely confused me as it was contradicting what Spring's documentation says.
Sources saying one thing:
From http://www.javaworld.com/article/2074123/java-web-development/transaction-and-redelivery-in-jms.html
A message is automatically acknowledged when it successfully returns from the receive() method. If the receiver uses the MessageListener interface, the message is automatically acknowledged when it successfully returns from the onMessage() method. If a failure occurs while executing the receive() method or the onMessage() method, the message is automatically redelivered.
From http://www2.sys-con.com/itsg/virtualcd/Java/archives/0604/chappell/index.html
With AUTO_ACKNOWLEDGE mode the acknowledgment is always the last thing to happen implicitly after the onMessage() handler returns. The client receiving the messages can get finer-grained control over the delivery of guaranteed messages by specifying the CLIENT_ACKNOWLEDGE mode on the consuming session.
Spring Docs saying other things:
From http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/listener/AbstractMessageListenerContainer.html
The listener container offers the following message acknowledgment options:
"sessionAcknowledgeMode" set to "AUTO_ACKNOWLEDGE" (default): Automatic message acknowledgment before listener execution; no redelivery in case of exception thrown.
"sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": Automatic message acknowledgment after successful listener execution; no redelivery in case of exception thrown.
"sessionAcknowledgeMode" set to "DUPS_OK_ACKNOWLEDGE": Lazy message acknowledgment during or after listener execution; potential redelivery in case of exception thrown.
"sessionTransacted" set to "true": Transactional acknowledgment after successful listener execution; guaranteed redelivery in case of exception thrown.
What I want to know is that why these sources are saying different things? If all are true then How do I know how/when my message will be acknowledged?
You missed out the key phrase from the abstract container javadocs...
The exact behavior might vary according to the concrete listener container and JMS provider used.
The most commonly used listener container used in Spring is the DefaultMessageListenerContainer which does exhibit that behavior - it is intended for use with transactions (either local or an external transaction manager), in order to have the ability to roll back an already acknowledged message. Its listener is invoked after the receive method, so the standard JMS auto-ack has already been applied. Any JmsTemplate operations on the thread can also use the same session - and thus can be part of the transaction.
On the other hand, the SimpleMessageListenerContainer uses a traditional MessageListener and exhibits the standard JMS behavior (the listener is called from the Consumer before receive() returns; thus exceptions will stop the ack).
I suggest you read the javadocs for those concrete implementations. From the SMLC...
This is the simplest form of a message listener container. It creates a fixed
number of JMS Sessions to invoke the listener, not allowing for dynamic
adaptation to runtime demands. Its main advantage is its low level of
complexity and the minimum requirements on the JMS provider: Not even the
ServerSessionPool facility is required.
See the AbstractMessageListenerContainer javadoc for details on acknowledge
modes and transaction options.
For a different style of MessageListener handling, through looped
MessageConsumer.receive() calls that also allow for transactional reception of
messages (registering them with XA transactions), see
DefaultMessageListenerContainer.
I will open up a JIRA issue for the docs on the abstract container because I can see that it might be misleading.

How did JMS implement redelivery function of onMessage?

As we known, if there is any exception is thrown at onMessage method of MessageListener, the JMS will try to consume that message again, which called "Redelivery".
But what I'm curious is how did it implement this feature? How did onMessage methods know if there is any exception is thrown? I know the answer may be a basic java knowledge. But unfortunately, I still have no idea on it.
So... is there anyone can clarify it?
This is specific to a particular JMS provider. But if your onMessage() method throws an exception, the code of the JMS provider that calls your onMessage() can just catch it, .e.g
try {
listener.onMessage(..);
} catch (Exception ex) {
//handle exception
}
And if an exception is caught, it can employ a strategy for redelivering the message. The client could send a message back to the JMS broker telling the broker that message delivery failed, and let the broker re-deliver that message.

Resources