2015-06-01, Sven Ehrke (@syendar)
Introduction
In a recent project we had two Grails based applications (frontend and backend application) which needed to exchange messages over MQ Series in a request response manner similar to HTTP.
To do this we used the Grails JMS Plugin.
Problem
The problem was that messages could get stuck in the queue when either the frontend or the backend went down and the queues got flooded. Even after the applications were available again the messages stayed in the queue because nobody was waiting for them anymore.
Solution
The solution to this problem is to set a time to live (TTL) on the messages. We did not find a way to configure this on the queue. Instead the TTL has to be set when the message is sent.
Frontend
In the frontend, the requesting side, it is easy a MessageProducer to actively to send the message is used anyway. So the only thing left to do is to set the TTL on the producer before sending the message:
MessageProducer producer
...
long ttlInMillis = 60000 // 60 seconds
producer.setTimeToLive(ttlInMillis)
producer.send(message)
Backend
The difficult part is finding out how to set the TTL for the returning message (when the backend responds to the frontend’s incoming message). Because sending the response is done by the framework you have to find the place where the send call is invoked. After digging around I found the place in Spring’s AbstractAdaptableMessageListener sendResponse method:
protected void sendResponse(Session session, Destination destination, Message response) throws JMSException {
MessageProducer producer = session.createProducer(destination);
try {
postProcessProducer(producer, response);
producer.send(response);
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}
What we would need to do is to set the TTL on the producer as we have done it in the frontend. Since this method is rather small and it’s visibility is protected it should be easy to subclass AbstractAdaptableMessageListener, redefine it and add the setting of the TTL.
Having a look at the plugin’s code showed that it already uses it’s own MessageListenerAdapter: PersistenceContextAwareListenerAdapter.
So our custom MessageListenerAdapter needs to extend PersistenceContextAwareListenerAdapter and will look as follows:
package org.myproject.jms
import javax.jms.Destination;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
class TTLListenerAdapter extends PersistenceContextAwareListenerAdapter {
@Override
protected void sendResponse(Session session, Destination destination, Message response)
throws JMSException {
MessageProducer producer = session.createProducer(destination);
try {
postProcessProducer(producer, response);
producer.setTimeToLive(60000) (1)
producer.send(response);
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}
}
-
The only line different to the original code (setting the TTL to 60 seconds)
The last thing to find out is how to tell the plugin to use TTLListenerAdapter. After some debugging I found that it can be configured as follows:
jms {
...
adapters {
standard {
clazz = 'org.myproject.jms.TTLListenerAdapter' (1)
...
}
...
}
}
-
This line tells the JMS plugin to use TTLListenerAdapter instead of the default PersistenceContextAwareListenerAdapter.
If you have configured more than one adapter simply configure the class for each one:
jms {
...
adapters {
standard {
clazz = 'org.myproject.jms.TTLListenerAdapter'
...
}
other {
clazz = 'org.myproject.jms.TTLListenerAdapter'
...
}
...
}
}
That’s it
I hope this post is useful if you have the same problem in one of your Grails based projects.
Based on Grails 2.4.4