FaridM
FaridM

Reputation: 63

Set message priority using Spring Boot, JMS and ActiveMq

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

Answers (3)

user3467108
user3467108

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

userbhavs
userbhavs

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

user2936416
user2936416

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

Related Questions