Spring JMS(ActiveMQ) delayed delivery of messages
Asked Answered
R

9

11

We're trying to set a delay on some JMS messages, so that a message will only be added to the queue/ received by the listener after x time. So far we've tried 2 approaches that didn't work.

1) According to the spring documentation, we can set the delivery delay on the JMSTemplate. This is the sample code we tried:

@Autowired
private JmsTemplate jmsTemplate;

...
long deliveryDelay = ...;
this.jmsTemplate.setDeliveryDelay(deliveryDelay);
this.jmsTemplate.convertAndSend(
                    queue.getName(),
                    event);
...

However, we get the following exception, even though our spring jms version is 4.0.5:

java.lang.IllegalStateException: setDeliveryDelay requires JMS 2.0

2) We also tried setting the delay on the message itself, but it looks like the delay was ignored, and the message was delivered immediately anyway.

@Component
public class MyMessageConverter implements MessageConverter {

...

@Override
public Message toMessage(Object eventObject, Session session) throws JMSException, MessageConversionException {

...
long deliveryDelay = ...;
objectMessage.setLongProperty(
                  ScheduledMessage.AMQ_SCHEDULED_DELAY,
                  deliveryDelay);
return objectMessage;
}
}

The jmsTemplate definition in the spring xml:

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="cachingConnectionFactory" />
    <property name="messageConverter" ref="myMessageConverter" />
    <property name="sessionTransacted" value="true" />
</bean>

Does anyone has any suggestions on what the problems are / other ideas on how to achieve delayed messaging? Thanks!

Reverie answered 4/8, 2014 at 14:31 Comment(3)
Your first approach will only work for JMS 2.0 compliant JMS brokers, ActiveMQ isn't (at the moment) one of them. The second option is only going to work if you also enabled scheduling on the JMS broker side else the property will do nothing.Doorman
Thanks @M.Deinum. I set schedulerSupport="true" in the activemq.xml and now it works.Reverie
The delayed message is stored in the client side?Norval
L
8

The comments give the answer. By default scheduled message support is disabled. You must enabled it in the broker XML configuration file as mentioned on the documentation page.

An example Broker tag with scheduler support enabled:

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">

You must of course restart the broker in order for configuration changes to take affect. Then when you send a message you need to add the JMS headers that tell the broker what type of delay you want.

message.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, scheduledDelay);
Lockjaw answered 4/8, 2014 at 16:45 Comment(2)
Hey ,@Tim Bish! I am wondering where should I define this line to enable scheduler support <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">. I checked the documentation you shared and it doesn't says anything. My inclination is conf/activem/xml, however, I couldn't find anything related to enabling schedulerSupport over there. Thanks !Nephralgia
ActiveMQ has its config file activemq.xml, you need to define it there.Eldest
G
1

Two things needs to be done to resolve this.

  • By default scheduled message support is disabled. We must enabled it in the broker XML configuration file.

broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">

  • Set the delay before sending the message.

    public void send(Object object) {
        log.info("put <" + object + ">");
        jmsTemplate.convertAndSend(QUEUE_NAME, object, m -> {
           m.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 10000);
           return m;
        });
    }
    
Gpo answered 30/1, 2019 at 5:48 Comment(1)
Could you tell me where in conf/activemq.xml, I should define this line ? <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">Nephralgia
S
0

Also according to method from ActiveMQ BrokerService class you should configure persistence to have ability to use scheduler functionality.

public boolean isSchedulerSupport() {
    return this.schedulerSupport && (isPersistent() || jobSchedulerStore != null);
}
Supercharger answered 25/11, 2014 at 10:28 Comment(1)
This actually depends on what broker version you are using, later releases do have an in memory scheduler store for brokers that run without persistence.Lockjaw
A
0
  jmsTemplate.convertAndSend(destination, message, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws JMSException {
                message.setIntProperty("JMS_OracleDelay", 200);
                return message;
            }
        });
Auld answered 23/9, 2017 at 18:23 Comment(1)
Could you explain how this code snippet answers the question?Impersonal
S
0
broker-url: vm://embedded?broker.persistent=true&broker.useShutdownHook=false&broker.schedulerSupport=true
Sotos answered 10/1, 2018 at 7:46 Comment(1)
Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please edit your answer to add some explanation, including the assumptions you’ve made.Ara
W
0

The JMS 2.0 is not supported in the activemq package. Try using artemis instead. Try replacing the package name from

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
        <version>1.5.6.RELEASE</version> 
    </dependency>

into

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-artemis</artifactId>
        <version>1.5.6.RELEASE</version> 
    </dependency>

and also add in the application.properties

    spring.artemis.mode=native
    spring.artemis.host=localhost
    spring.artemis.port=61616
    spring.artemis.user=admin
    spring.artemis.password=admin
Washday answered 31/1, 2018 at 19:46 Comment(0)
L
0

Documentation http://activemq.apache.org/delay-and-schedule-message-delivery.html

Example: after 10 sec received message by consumer

public void send(Object object) {
    log.info("put <" + object + ">");
    jmsTemplate.convertAndSend(QUEUE_NAME, object, m -> {
        m.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 10000);
        return m;
    });
}
Lyric answered 23/1, 2019 at 9:50 Comment(0)
M
0
  1. I have added schedulerSupport=true in active mq configuration xml. Please dont forget to restart the active mq server after configuration changes. Once you restart and login to "Scheduled" tab on admin console of activemq, you would see scheduled message details.

  2. jmsTemplate.setDeliveryDelay did not work for me so I added the below piece of code : used

    jmsTemplate.convertAndSend(queueName, object, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws JMSException {
                message.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, scheduledDelay);
                return message;
            }
        });

Please note : It was not working for me in the beginning. But what was needed was an restart on activeMQ server to reflect the changes in config

Mechanical answered 25/11, 2019 at 15:3 Comment(0)
U
0

You can also define a new, delaying JmsTemplate which you can autowire where needed:

@Bean
public JmsTemplate jmsTemplate(){
    JmsTemplate jmsTemplate = new JmsTemplate();
    jmsTemplate.setConnectionFactory(connectionFactory());
    return jmsTemplate;
}

@Bean
public JmsTemplate delayingJmsTemplate(){
    JmsTemplate jmsTemplate = new JmsTemplate();
    jmsTemplate.setDeliveryDelay(1000);
    jmsTemplate.setConnectionFactory(connectionFactory());
    return jmsTemplate;
}

...

@Autowired
private JmsTemplate jmsTemplate;

@Autowired
private JmsTemplate delayingJmsTemplate;
Unaccomplished answered 20/6, 2023 at 9:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.