Message redelivery and DLQ tips for ActiveMQ 3.x

by Dejan Bosanac

I've played a lot with the ActiveMQ JMS provider these days and I think that it is a very good product. Its major weakness is a lack of the proper documentation, which makes an initial introduction to ActiveMQ to last longer that you would expect. In this post I will explain a workaround for some of the issues that I have found, that could save you some time (in case that you get in the same situation as I did).
First of all, let me explain what I have tried to achieve. In my project I wanted to have a queue that would receive messages and process them. In case that message processing throws an exception, I wanted the broker to try three more times before quitting and send that message to the dead letter queue (DLQ).
The simplified listener method that should do the work (when the system is configured correctly) could look like this.
public void onMessage(Message message) {
try {
process(message);
session.commit();
} catch (Exception e) {
session.rollback();
}
}

The message is processed and if all goes smooth the session is committed, which tells the broker that the message is successfully consumed. If any exception occurs, the session is rolled back, which should trigger the broker to send it again (if it complies with its redelivery policy).
As you can assume this solution didn't work, and here's why:
  • Redelivery policy configuration does not work in 3.1 - You can configure broker to use certain redelivery policy. It could be done both in the XML configuration file, by specifying the

    <redeliveryPolicy backOffMode="true" maximumRetryCount="2"/>

    tag. You can also try to do this programatically if you are using an embedded broker. But none of this doesn't seem to work in 3.1 version.
    So before you spend a lot of time trying to make this work in 3.1, please upgrade to 3.2

  • DLQs are working only for topics with durable subscribers - as far as I could find in various forums (and by browsing through the code), DLQ mechanism works only for topics that have durable subscribers. So even if you make redelivery policy to work (in 3.2), DLQ mechanism will not work for ordinary queues. I'm not sure why this is, but it certainly didn't what I needed.

  • Since DLQs are just ordinary queues, here's a tip on how you can avoid these two issues:
    public void onMessage(Message message) {
    try {
    process(message);
    session.commit();
    } catch (Exception e) {
    try {
    if (((ActiveMQMessage)message).getDeliveryCount() >= maximumRetryCount) {
    session.commit();
    DeadLetterPolicy dlp = new DeadLetterPolicy();
    String dlq = dlp.getDeadLetterNameFromDestination((ActiveMQDestination)message.getJMSDestination());
    MessageProducer producer = session.createProducer(session.createQueue(dlq));
    producer.send(message);
    session.commit();
    } else {
    session.rollback();
    }
    } catch (JMSException jmse) {
    //log the exception
    }
    }
    }

    Our previous example is changed so that now we have an improved exception handling. When the message processing throws an exception, we first check how many times the message has been redelivered. In case that the redelivery count didn't reach the maximum, we should rollback the transaction (causing the broker to redeliver the message). Otherwise, we create an appropriate DLQ and send the message to it manually. We should first commit the transaction in order to instruct the broker that the message is finally consumed.
    The one of the drawbacks of this workaround is that maximum retry count must be configured for the consumer (listener) as well, since it completes this job now. Of course, if you are using Spring or some other project that enables easy application configuration this is not such a big deal.

    Have you expirienced some of these limitations? What were your workarounds?


    2 Comments

    franklinsmith
    2006-01-05 07:57:45
    JMs redelivery and DLQ
    I've had to use similar techniques. Another issue to consider is that unless you use separate sessions for input and output messages, your initial commit in the catch block will send messages that should be rolled back. For JMS broker independence you may also want to abstract the access to the redelivery count (a nonstandard JMS feature). The redelivery count can be implemented in a portable way using a message property but this solution is obviously little complicated than the ActiveMQ-specific one.
    mwaschkowski
    2006-03-07 10:21:03
    source
    can you post your full source?