Reputation: 63
I'm trying to send messages with different priority with the JmsTemplate using Spring Boot and ActiveMQ and it's not working.
I tried this :
MessageCreator mc = session -> {
TextMessage tm = session.createTextMessage("hello");
tm.setJMSPriority(6);
return tm;
};
jmsTemplate.send((Queue) () -> "box", mc);
The priority inside the ActiveMQ broker is still 4 (default value).
The only way I found to actually change the priority for a message is by change the priority at the JmsTemplate level.
jmsTemplate.setPriority(3);
The problem here is, now, all messages sent after that will have the priority 3. I know I can reset the JmsTemplate priority after each send, but it's not "clean" and what about concurrency?
How can I set the priority for each message and get the message with the highest priority using @JmsListener?
Upvotes: 3
Views: 4529
Reputation: 1
Update the JmsTemplate.QosSettings
field. By default JMSTemplate sets messages priority field to 4. The code bellow will show how you can change the messages priority. See details
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
jmsTemplate.setPubSubNoLocal(false);
QosSettings qosSettings = new QosSettings(1, 5, 0); //deliveryMode =1 priority=5 and timeToLive=0
jmsTemplate.setQosSettings(qosSettings);
It's also possible to use the JmsTemplate.setPriority() method. Be careful if you retrieve your jmsTemplate from Spring's context and it used for another integrations. You can break a contract with a system that you send a message with changed priority.
Upvotes: 0
Reputation: 1
In addition to answer provided by user2936416, you also have to explicity add below property in your consumer's jms config along with broker url, username , passwords and other properties
activeMQConnectionFactory.setMessagePrioritySupported(true);
Upvotes: 0
Reputation: 123
I just ran into the same issue.
I tested your point about setting the priority of the jmsTemplate and your assumption is correct. It is not handled properly with concurrency.
The solution I found that works (albeit not ideal) is to extend JmsTemplate and override the doSend method to copy the JmsPriority from the message to the producer. This isnt ideal, extending the class make break over spring boot releases (I've tested this on 2.1.7) and there are some additional steps to register the new JmsTemplate. But it does work and I've tested it under load.
Steps.....
Create a new class that extends JmsTemplate overriding the doSend method to copy the priority from the message
import java.io.Serializable;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import org.springframework.jms.core.JmsTemplate;
public class RcsJmsTemplate extends JmsTemplate implements Serializable {
public RcsJmsTemplate() {
}
public RcsJmsTemplate(ConnectionFactory connectionFactory) {
super(connectionFactory);
}
/**
* Actually send the given JMS message.
*
* AF: EXTENDED TO COPY THE PRIORITY FROM THE MESSAGE TO THE PRODUCER
*
* @param producer the JMS MessageProducer to send with
* @param message the JMS Message to send
* @throws JMSException if thrown by JMS API methods
*/
@Override
protected void doSend(MessageProducer producer, Message message) throws JMSException {
if (getDeliveryDelay() >= 0) {
producer.setDeliveryDelay(getDeliveryDelay());
}
producer.send(message, getDeliveryMode(), message.getJMSPriority(), getTimeToLive());
}
}
Add a bean (to your App.java, or appropriate config class) You may not need to pass through a message converter (Im using Jackson in my project) There may also be other configurations you need to apply to the new JmsTemplate.
@Bean
public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
RcsJmsTemplate rcsJmsTemplate = new RcsJmsTemplate(connectionFactory);
rcsJmsTemplate.setMessageConverter(messageConverter);
return rcsJmsTemplate;
}
Then as in your question set the JmsPriority attribute of the message. Your using a MessageCreator but in my project Im using a message post processoe
public void convertAndSendWithPriority(JmsTemplate jmsTemplate, String destination, Object message, int priority) {
jmsTemplate.convertAndSend(destination, message, (Message jmsMessage) -> {
jmsMessage.setJMSPriority(priority);
return jmsMessage;
});
}
For completeness you should add the property: spring.jms.template.qos-enabled=true
Thats it. Hope it helps (Actually I hope someone comes up with a better answer) Thanks
Upvotes: 4