Definition and usage

JMS message delivery delaying is simply sending a JMS message, but wait a defined amount of time before handling it and triggering the associated treatment. Curiously, this feature is not standardized in JMS. Most JMS providers have their own implementation to handle message delaying, but how to do when you need this feature in a portable way ?
In most case, you don’t have the choice of the JMS provider to use, since it depends of the softwares deployed in your company. Moreover, you have sometimes a different application server between development environments and production environments (for any cost or philosophical reasons).

In these cases, having a portable way to schedule JMS message can be usefull.

Principle

The solution is quite simple : we use the message selector feature included in the JMS API. Message selectors allows to consume only messages that satisfy a defined expression. In our case, the expression is simple :

_DeliverAfter_ <= System.currentTimeMillis()

where « _DeliverAfter_ » is a message property containing the value after which deliver the message.


Implementation

Using Spring

Spring is widely used and provides a very good support of JMS.
To send message, I created a subclass of Spring’s JmsTemplate which adds a timestamp parameter to each existing convertAndSend() method:

public class ScheduleJMSTemplate extends JmsTemplate {
//...
    public void convertAndSend(Object message, long scheduleTimestamp) throws JmsException {
        MessagePostProcessor postProcessor =
        new ScheduleMessagePostProcessor(scheduleTimestamp);
        super.convertAndSend(message, postProcessor);
    }

//...
}

To receive delayed messages, I extended DefaultMessageListenerContainer. The ScheduleDefaultMessageListenerContainer class acts as a drop-in replacement of DefaultMessageListenerContainer.

The full source code is available for download here : SpringJMSSchedule

D’oh ! I’m not using Spring

So let’s look how to do with plain JMS API :

Sending a message :

public void producesMessage(Session session, Destination destination) throws JMSException {
	MessageProducer producer = session.createProducer(destination);
	Message message = session.createTextMessage("I will be delivered in one week...");

	// Delay my message for 1 week (time in ms)
	long scheduleTimestamp = System.currentTimeMillis() + 7 * 24 * 60 * 1000;
	message.setLongProperty("_DeliverAfter_", scheduleTimestamp);

	producer.send(message);
}

The code above is straightforward : just create the JMS message as usual, and set the timestamp property.

Receiving the message :

	public void consumesMessage(Session session, Destination destination) throws JMSException {
		while (myCondition) {
			String messageSelector = "_DeliverAfter_ <= " + System.currentTimeMillis();
			MessageConsumer consumer = session.createConsumer(destination, messageSelector);

			Message message = consumer.receive();
			// Do something usefull with the message...
		}
	}

Consuming the message is simple : the MessageConsumer is created with a message selector that use the property set in the message.

Pitfalls

There’s a few pitfalls to avoid when using message delivery delaying :

  • Check message persistancy. You must ensure that your messages are saved in a persistent way, especially if you’re using long delays, otherwise you could lose some messages.
  • Use TextMessage. You must store messages in a format that could always be read, even if the version of application that send the message is different from the version that will read the message. In particular, you must avoid using ObjectMessage since its content is tightly coupled with classes used when creating the message. If your classes change before reading the message, you can encounter deserialization errors.