This example shows you how to define and deal with dead letter messages.
Messages can be delivered unsuccessfully (e.g. if the transacted session used to consume them is rolled back). Such a message goes back to the JMS destination ready to be redelivered. However, this means it is possible for a message to be delivered again and again without any success and remain in the destination, clogging the system.
To prevent this, messaging systems define dead letter messages: after a specified unsuccessful delivery attempts, the message is removed from the destination and instead routed to a dead letter address where they can be consumed for further investigation.
The example will show how to configure ActiveMQ to route a message to a dead letter address after 3 unsuccessful delivery attempts.
The example will send 1 message to a queue. We will deliver the message 3 times and rollback the session every time.
On the 4th attempt, there won't be any message to consume: it will have been moved to a dead letter address.
We will then consume this dead letter message.
Dead letter addresses and maximum delivery attempts are defined in the configuration file activemq-configuration.xml:
<address-setting match="jms.queue.exampleQueue">
<dead-letter-address>jms.queue.deadLetterQueue</dead-letter-address>
<max-delivery-attempts>3</max-delivery-attempts>
</address-setting>
This configuration will moved dead letter messages from exampleQueue
to the deadLetterQueue
.
ActiveMQ allows to specify either a Queue
by prefixing the dead-letter-address
with jms.queue.
or a Topic
by prefixing with jms.topic.
.
In this example, we will use a Queue
to hold the dead letter messages.
The maximum attempts of delivery is 3
. Once this figure is reached, a message is considered a dead letter message and is moved to
the deadLetterQueue
.
Since we want to consume messages from this deadLetterQueue, we also need to add a JNDI binding to perform a lookup. This is configured in activemq-jms.xml
<queue name="deadLetterQueue">
<entry name="/queue/deadLetterQueue"/>
</queue>
To run the example, simply type mvn verify -Pexample
from this directory
client-jndi.properties
file in the directory ../common/config
InitialContext initialContext = getContext();
Queue queue = (Queue) initialContext.lookup("/queue/exampleQueue");
ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("/ConnectionFactory");
connection = cf.createConnection();
Session session = connection.createSession(true, 0);
MessageProducer messageProducer = session.createProducer(topic);
TextMessage message = session.createTextMessage("this is a text message");
producer.send(message);
session.commit();
We will now consume the message from the queue 3 times and roll back the session every time
MessageConsumer messageConsumer = session.createConsumer(queue);
connection.start();
TextMessage messageReceived = (TextMessage)messageConsumer.receive(5000);
System.out.println("1st delivery from " + queue.getQueueName() + ": " + messageReceived.getText());
session.rollback();
messageReceived = (TextMessage)messageConsumer.receive(5000);
System.out.println("2nd delivery from " + queue.getQueueName() + ": " + messageReceived.getText());
session.rollback();
messageReceived = (TextMessage)messageConsumer.receive(5000);
System.out.println("3rd delivery from " + queue.getQueueName() + ": " + messageReceived.getText());
session.rollback();
Since the queue was configured to move messages to the deadLetterQueue
after 3
unsuccessful delivery attempts,
the message won't be in the queue
anymore
messageReceived
will be null
messageReceived = (TextMessage)messageConsumer.receive(5000);
System.out.println("4th delivery from " + queue.getQueueName() + ": " + messageReceived);
We have configured ActiveMQ to send any dead letter messages to the deadLetterQueue
.
We will now consume messages from this queue and receives the dead letter messages.
Queue deadLetterQueue = (Queue)initialContext.lookup("/queue/deadLetterQueue");
MessageConsumer deadLetterConsumer = session.createConsumer(expiryQueue);
messageReceived = (TextMessage)deadLetterConsumer.receive(5000);
System.out.println("Received message from " + deadLetterQueue.getQueueName() + ": " + messageReceived.getText());
JMS does not specify the notion of dead letter destinations and messages. From JMS point of view, the message received from the dead letter queue
is a different message than the message removed from the queue after the unsuccessful delivery attempts:
the messages have the same content (properties and body) but their JMS headers differ.
ActiveMQ defines additional properties for messages received from a dead letter destination
System.out.println("Destination of the message: " + ((Queue)messageReceived.getJMSDestination()).getQueueName());
_AMQ_ORIG_ADDRESS
property
System.out.println("*Origin destination* of the message: " + messageReceived.getStringProperty("_AMQ_ORIG_ADDRESS"));
session.commit();
finally
block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects
finally
{
if (initialContext != null)
{
initialContext.close();
}
if (connection != null)
{
connection.close();
}
}