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
Related
I have some theoretical question regarding "message loops"; particularly returning result of operations happening in a message loop that runs in a different thread. I have the situation where I'm having a TCP server listening for incoming messages. For each incoming message the server will authenticate the client who sent the message and two things may happen:
If the authenticated client has an attached handler the received message will be passed to the handler's message queue.
If the client has no handler a new one will be created and the same as above (the message will be passed to its message queue).
The handler is currently an object implementing the Callable interface so that it'll run in a different thread and its simple enough the get the result of the operation. Now for my problem: Each handler can have N amount of messages to be processed. The handler has a "message loop" like functionality that runs until a timeout occurs - a timeout in this case would be the socket's idle time reaching a predefined treshold. What I would like to know, how can I get Java to return a value from within the message loop without actually terminating the thread. Something like the following:
while (true) {
if (expired(socket))
break; // the callable will finish the call() method.
// get the first item from the queue.
message = messageQueue.poll();
result = process(message);
// I want to return the result to the caller which is in a different thread.
}
Now obviously a return statement would stop the message loop and if the messageQueue contains more messages they'll be lost. Another naive approach would be to use a callback-like mechanism, which requires an extra object + I still need to synchronize the caller with the Callable in the background thread. Something like wait & notify although I have K threads running in the background.
What would be the sophisticated way to handle this situation of returning results of operations from within a message-loop in a different thread, without terminating the thread itself?
#Edit:
I'll give a description of the whole process so that it clarifies what is happening here.
A client sends a message (xml string) to the application through tcp sockets.
The application authenticates the client, and if the client has no associated handler it'll create one.
The app will push the message to the queue of the handler.
The handler runs in a separate thread waiting for incoming messages from clients they're associated with, they MUST NOT handle messages for other clients.
When the handler picks up a message it'll transform it into a SOAP message and will forward it to another system through TCP socket.
When the handler recieves the response it needs to delegate it back to the caller without terminating its message-loop.
So the caller is something like a Dispatcher dispatching messages to the threads that are running the handlers associated with the sender of the message. It also collects the response from the handlers and sends them back to the correct clients.
Each handler, currently has their own message queue where only those messages are pushed which the particular handle has to process. When a handler starts up, it'll open a TCP socket to the target system where they'll forward the incoming messages after transformations were applied. When the handler reaches the maximal allowed idle time (The socket were opened without sending a request) the socket will be closed and the message-loop stopped. At this point the handler will finish its execution. the purpose of this, is to have a socket for each individual clients through which they can send multiple requests without the need for the target system to do another authentication.
Few options/questions come to mind:
Is there a problem to terminate the thread, check the returned result and then re-submit this task to the same thread pool? You will get a result, analyze it, and then resubmit to the pool and continue the work
As this thread runs, it can submit the statuses to a different ("external") queue which is analyzed outside this thread. An independent thread always run and check this queue
That's as far as I could think on how to...
It depends on...
If you want to return simple type you can use a thead safe result queue (global or by caller).
Propably thread pool will be more suitable in your case.
I belive that the most universal way is callback mechanism.
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."
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.
In the following this, there is code for a basic JMS synchronous receiver.
The question I have is this: When a message is delivered to the queue, because it is synchronous, it will automatically be available via the receiver.receive method. Is this correct?
If this is so, then after msg1 arrives on queue, while we are still processing receiver.receive(). what happens if msg2 arrives on queue.
since the call receiver.receive() has not returned, will it be able to still receive the message # 2?
msg2 will remain on the queue until another consumer calls receive().