stephanruhl stephanruhl - 16 days ago 6
Java Question

JMS and MDB with setRollbackOnly

I have a java class which consumes messages from a queue, sending HTTP calls to some urls. I have made some search on google and also on stackoverflow (and really sorry if i have missed any sources mentioning about the problem) but couldnt find anything in details about setRollbackOnly call.

My question is... in case I rollback, the message which is consumed from the queue will be blocking the rest of the queue and will be looping until it is processed successfully or it will be requeued at the end of the current queue?

My code which I use for consuming from the queue and sending HTTP calls is below and the whole application is running on Glassfish server:


public class RequestSenderBean implements MessageListener
{
@Resource
private MessageDrivenContext mdbContext;

public RequestSenderBean(){}

public void onMessage(final Message message)
{
try
{
if(message instanceof ObjectMessage)
{

String responseOfCall=sendHttpPost(URL, PARAMS_FROM_MESSAGE);

if(responseOfCall.startsWith("Success"))
{
//Everything is OK, do some stuff
}
else if(responseOfCall.startsWith("Failure"))
{
//Failure, do some other stuff
}

}
catch(final Exception e)
{
e.printStackTrace();
mdbContext.setRollbackOnly();
}
}
}

atc atc
Answer

This is fundamental JMS/messaging knowledge.

Queues implement "load balancing" scenarios, whereby a message hits a queue and is dequed to be processed by one consumer. Increasing the number of consumers increases potential throughput of that queue's processing. Each message on a queue will be processed by one and only one consumer.

Topics provide publish-subscribe semantics: all consumers of a topic will receive the message that is pushed to the topic.

With that in mind, once a message is dequed and handed (transactionally) to a consumer, it is by no means blocking the rest of the queue if it is asynchronous (as is the case with MDBs).

As the Java EE Tutorial states:

Message Consumption

Messaging products are inherently asynchronous: There is no fundamental timing dependency between the production and the consumption of a message. However, the JMS specification uses this term in a more precise sense. Messages can be consumed in either of two ways:

Synchronously: A subscriber or a receiver explicitly fetches the message from the destination by calling the receive method. The receive method can block until a message arrives or can time out if a message does not arrive within a specified time limit.

Asynchronously: A client can register a message listener with a consumer. A message listener is similar to an event listener. Whenever a message arrives at the destination, the JMS provider delivers the message by calling the listener’s onMessage method, which acts on the contents of the message.

Because you use a MessageListener which is by definition asynchronous, you are not blocking the queue or its subsequent processing.

Also from the tutorial is the following:

Using Session Beans to Produce and to Synchronously Receive Messages

An application that produces messages or synchronously receives them can use a session bean to perform these operations. The example in An Application That Uses the JMS API with a Session Bean uses a stateless session bean to publish messages to a topic.

Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a receive call in an enterprise bean. Instead, use a timed synchronous receive, or use a message-driven bean to receive messages asynchronously. For details about blocking and timed synchronous receives, see Writing the Clients for the Synchronous Receive Example.

As for message failure, it depends on how your queue is configured. You can set error-queues (in the case of containers like Glassfish or Weblogic) that failed messages are pushed to for later inspection. In your case, you're using setRollbackOnly which is handled thus:

7.1.2 Coding the Message-Driven Bean: MessageBean.java

The message-driven bean class, MessageBean.java, implements the methods setMessageDrivenContext, ejbCreate, onMessage, and ejbRemove. The onMessage method, almost identical to that of TextListener.java, casts the incoming message to a TextMessage and displays the text. The only significant difference is that it calls the MessageDrivenContext.setRollbackOnly method in case of an exception. This method rolls back the transaction so that the message will be redelivered.

I recommend you read the Java EE Tutorial as well as the Enterprise Integration Patterns book which covers messaging concepts in good detail that's also product/technology-agnostic.

Comments