JMS Session interface has two similar methods: rollback and recover. They are used in different ack mode: rollback in transaction and recover in client ack. But except that, what is the main difference of these two methods.
rollback: tells the broker that current client will not process current message(s). Broker policy then dictates if it will redeliver to the same (or other) consumer or roll the messages to a dead letter queue
recover: instructs the broker to redeliver all unacknowledged messages to the current session
Related
I'm new to JMS and would appreciate your help.
Everything I read about JMS gives me the impression that the object that "holds a transaction" is a session object, meaning that when we "session.commit()", everything that has happened since last commit is settled.
My question regards, how to handle the patter where I want to "transact messages", not the session. Imagine that you want to receive a large number of trades in a very concurrent scenario, and upon receiving any individual message and processing you'd like to "message.commit()". How can we implement this scenario ?
Would I have to keep creating QueueSessions for every message that I wanted to dequeue ? Isn't there too much overhead ?
Thanks in advance
It is correct, a JMS Session object is responsible for managing transactions. The Session.Commit() call commits all messages received/sent in that session. Similarly the Session.Rollback() rolls back messages in a session.
For your requirement you can use a non-transacted session with CLIENT_ACKNOWLEDGE option. Like:
createSession(false, Session.CLIENT_ACKNOWLEDGE);
A CLIENT_ACKNOWLEDGE session let's you do a message.Acknowledge which tells the messaging provider to remove that message from queue/topic.
But you have to keep in mind that the implementation of message.Acknowledge is implementation dependent. Some messaging providers allow per message acknowledge where as others do a acknowledge all messages. In per message acknowledge, only the message on which acknowledge is called, will be removed from messaging provider. All other messages received and not acknowledged will not be removed. Where as in the 'acknowledge all' type of implementation, calling acknowledge on one message will acknowledge all messages received before. This is same as doing a session.commit in a transacted session.
I am going to use a Session to commit the read of a JMS message after it (and any corresponding write) has successfully completed.
However, if I have an error, and have to do a rollback, I would like to process new messages first, rather than the one that had (caused???) the error that had to be rolled back. I want to eventually reprocess the failed message, but not to fail over and over while other yet-unseen messages stall behind it, waiting for action to remove the offending message or fix the environment that made it fail.
Is this automatic? (will be using Sonic MQ, if that matters). If so, the rest of this question is moot.
Do I need to, or can I even, reset the priority of the failed message to push it further back in the queue (behind other pending messages, if any)? If I need to reset the priority, how do I make that "stick", given that I would have rolled back the transaction that initially read the message in question.
I am not aware of feature in Sonic MQ which supports your requirements out-of-the-box, but there are other options:
Use a second queue for failed messages. So failed messages are sent again on another queue. Processing could start, if the first queue is empty, for example.
Resend the message on the same queue (with same or even with a lower priority)
In both cases, after the message has been sent, there is a normal commit on the main queue.
Related: Message processing with priorities. A quote from James Shek's answer:
JMS Spec states: "JMS does not require that a provider strictly implement priority ordering of messages; however, it should do its best to deliver expedited messages ahead of normal messages."
Is there any transaction isolation level available in JMS queue like in JDBC ?
More specifically in transitional mode, when a client consumes a message and has not committed yet, will the next client consume the next message? If the 1st client rolls back transition, where the message placed in the queue, I think it will be placed where it was earlier.
Short answer: no.
JMS sending is either transactional or not, which means the message is "committed" (visible for the broker and MDBs) when senders transaction commits or immediately (even if client sending transaction rollbacks later).
JMS receiving has simple on/off transactions as well: message is marked as received either when it's picked up by MDB (non-transactional) or when MDB returns without exception (transactional).
Transaction isolation is about visibility of DB table changes. JMS messages are immutable, you send it once and you cannot change it.
How to ensure message acknowledge deletes only messages upto the message on which acknowledge is called in a jms broker.
Currently I have a system which consumes from a jms queue and partially processes it.Sometime later a batch of these messages gets persisted by a different thread. I need to acknowledge on messages now. But the problem is I have to stop consuming the messages, otherwise acknowledging a previously received message will also acknowledge all other subsequent messages received.
In other words suppose I have 10 messages in a queue. I consume 7 of them, and then acknowledge on 5th message. This in turn removes all 7 messages received by consumer from the queue.Is there a way to only acknowledge and remove messages from queue till 5th message.
EDIT: I have tried creating two sessions and consuming from different sessions, but (with apache qpid atleast) this performs inconsistently. By inconsistently I mean, sometimes during the test it so happens that one consumer is able to receive messages, while the other doesn't receive at all, no matter how long you wait. This would have worked for me as a solution, but because of inconsistency can't use this as a solution.
I understand this post is old, but this answer should benefit those who stumble upon it later.
If you'd like fine grained control of which messages you'd like to acknowledge, the individual acknowledge method should help you. Using this acknowledgement mode you can ack individual messages in a session. Messages that have not been ack-ed will be redelivered.
This is not part of the spec, but most queue providers support it outside the spec.
Oracle
For more flexibility, Message Queue lets you customize the JMS
client-acknowledge mode. In client-acknowledge mode, the client
explicitly acknowledges message consumption by invoking the
acknowledge() method of a message object.
The standard behavior of
this method is to cause the session to acknowledge all messages that
have been consumed by any consumer in the session since the last time
the method was invoked. (That is, the session acknowledges the current
message and all previously unacknowledged messages, regardless of who
consumed them.)
In addition to the standard behavior specified by JMS, Message Queue
lets you use client-acknowledge mode to acknowledge one message at a
time.
public interface com.sun.messaging.jms.Message {
void acknowledgeThisMessage() throws JMSException;
void acknowledgeUpThroughThisMessage() throws JMSException;
}
ActiveMQ
One can imagine other acknowledge modes that would be useful too, for
example: CONSUMER_ACKNOWLEDGE where Message.acknowledge() would
acknowledge only messages received up on a particular MessageConsumer,
or CONSUMER_CHECKPOINT_ACKNOWLEDGE where Message.acknowledge() would
acknowledge only messages received up to and including the Message
instance on which the method was called.
But without embarking on all these various different possibilities,
would it be possible to consider just adding INDIVIDUAL_ACKNOWLEDGE
mode? This alone would make it possible for multithreaded applications
to achieve whatever behaviors they need.
connection.createQueueSession(false, ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);
I have not used QPID personally, however the documentation hints to the fact that individual message acks are possible.
Examples
# acknowledge all received messages
session.acknowledge
# acknowledge a single message
session.acknowledge :message => message
While processing a batch you can ack each message that is received and processed. If you encounter an exception, do not ack the message.
Acknowledging a message will make the queue manager to remove that message plus all other messages received before that message. It should not remove the messages which have not yet been received by an application. You may want to check your application on how you are acknowledging a message.
How do I acknowledge a message when I am using a message listener?
I get the following error when I try to do an acknowledge in my message listener.
A synchronous method call is not permitted when a session is being used asynchronously: 'acknowledge'
You're talking about JMS messages acknowledgement as in Message.acknowledge()?
That error seems a little odd. If you aren't using transactions or auto-acknowledge, I'd think you need to call that method. And if you're doing async listening, where are you doing to do it aside from the onMessage() method?
Is this call being done in the same thread that got the onMessage() call? In other words, in onMessage() or in some method called from onMessage()? If not, you're breaking the thread rules of JMS. Sessions and producers/consumers and anything further down (like Messages) aren't thread safe. You need to make sure you're not touching them from multiple threads. If you're in the middle of an onMessage() call and you somehow arrange another thread to do that Message.acknowledge() call, you deserve to fail because of the thread problem. If so, move that call back on the same thread that onMessage() is running in.
This is an example for Queue Session
session = connection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
Only if
if (session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) //
Then can we have
message.acknowledge();
Check the Message class here (http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Message.html)
To amplify the first answer a bit for posterity: The OP probably created his session with acknowledgement mode set to Session.AUTO_ACKNOWLEDGE, which means the provider automatically acknowledges the messages as they are delivered on the connection (for synchronous delivery) or after your MessageListener#onMessage() is called (for asynchronous delivery).
He got the exception because his explicit call to Message#acknowledge() is not valid in this mode. As Buhake Sindi points out, if you wish to manually acknowledge messages, you must choose Session.CLIENT_ACKNOWLEDGE when you set up the session from which the MessageConsumer will be created. Then, each time you call Message#acknowledge(), the current message, along with any other delivered but unacknowledged messages delivered to this session/consumer, will be acknowledged back to the broker.
An asynchronous message, by definition, is not expected to be acknowledged at the protocol level. If you want an acknowledgement you must build it into your application, at which point the questions is why aren't you using a synchronous scheme.
Check to see if your session requires acknowledgement by using getAcknowledgeMode() method off the session, if it does then just call the acknowledge() method on the message itself