1

I have a simple Spring application for JMS Producer/Subscriber using ActiveMQ with below configuration :

Application Context xml :

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616" />
    <property name="userName" value="user" />
    <property name="password" value="password" />
</bean>
<bean id="messageDestination" class="org.apache.activemq.command.ActiveMQTopic">
    <constructor-arg value="messageQueue1" />
</bean>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE">
    </property>
</bean>

<bean id="springJmsProducer" class="SpringJmsProducer">
    <property name="destination" ref="messageDestination" />
    <property name="jmsTemplate" ref="jmsTemplate" />
</bean>

<bean id="springJmsConsumer" class="SpringJmsConsumer">
    <property name="destination" ref="messageDestination" />
    <property name="jmsTemplate" ref="jmsTemplate" />
</bean>

and Below is Spring producer

public class SpringJmsProducer {
private JmsTemplate jmsTemplate;
private Destination destination;

public JmsTemplate getJmsTemplate() {
    return jmsTemplate;
}

public void setJmsTemplate(JmsTemplate jmsTemplate) {
    this.jmsTemplate = jmsTemplate;
}

public Destination getDestination() {
    return destination;
}

public void setDestination(Destination destination) {
    this.destination = destination;
}

public void sendMessage(final String msg) {
    jmsTemplate.send(destination, new MessageCreator() {
        public Message createMessage(Session session) throws JMSException {
            return session.createTextMessage(msg);
        }});        
 }
}

below is Spring Consumer:

public class SpringJmsConsumer {
private JmsTemplate jmsTemplate;
private Destination destination;

public JmsTemplate getJmsTemplate() {
    return jmsTemplate;
}

public void setJmsTemplate(JmsTemplate jmsTemplate) {
    this.jmsTemplate = jmsTemplate;
}

public Destination getDestination() {
    return destination;
}

public void setDestination(Destination destination) {
    this.destination = destination;
}

public String receiveMessage() throws JMSException {
    TextMessage textMessage =(TextMessage) jmsTemplate.receive(destination);        
    return textMessage.getText();
 }
}

Issue : When i start producer and post messages, and then i start consumer, Consumer is not reading old messages but only reading messages posted after consumer was started. Could anyone please help me how to make this durable subscriber so that messages in queue which are not acknowledged should be read by consumer and also i need to implement Synchronous Consumer not Asynchronous.

I have tried all possible solution but none is working. Any help is highly appreciated

Sandip Jangra
  • 145
  • 2
  • 9

1 Answers1

3

if you want consumer receive messages sent to the topic before he starts you have 2 choice :

1. Use Activemq Retroactive Consumer

Background A retroactive consumer is just a regular JMS Topic consumer who indicates that at the start of a subscription every attempt should be used to go back in time and send any old messages (or the last message sent on that topic) that the consumer may have missed.

See the Subscription Recovery Policy for more detail.

You mark a Consumer as being retroactive as follows:

topic = new ActiveMQTopic("TEST.Topic?consumer.retroactive=true");

http://activemq.apache.org/retroactive-consumer.html

2. Use Durable Subscribers :

Note that the Durable Subscriber receive messages sent to the topic before he starts at the 2nd run

http://activemq.apache.org/manage-durable-subscribers.html

This is possible with DefaultMessageListenerContainer Asynchronously

<bean id="jmsContainer" destroy-method="shutdown"
    class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destination" ref="messageDestination" />
    <property name="messageListener" ref="messageListenerAdapter" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
    <property name="subscriptionDurable" value="true" />
    <property name="clientId" value="UniqueClientId" />
</bean>

<bean id="messageListenerAdapter"
    class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg ref="springJmsConsumer" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
</bean>

AND Update your consumer :

public class SpringJmsConsumer implements javax.jms.MessageListener {

    public void onMessage(javax.jms.Message message) {
        // treat message;
        message.acknowledge();
    }
}

UPDATE to use

if you want a Synchronous Durable Subscriber, an example

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;

public class SpringJmsConsumer {

    private Connection conn;
    private TopicSubscriber topicSubscriber;

    public SpringJmsConsumer(ConnectionFactory connectionFactory, Topic destination ) {
        conn = connectionFactory.createConnection("user", "password");
        Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
        topicSubscriber = session.createDurableSubscriber(destination, "UniqueClientId");
        conn.start();
    }

    public String receiveMessage() throws JMSException {
        TextMessage textMessage = (TextMessage) topicSubscriber.receive();
        return textMessage.getText();
    }
}

And update springJmsConsumer

<bean id="springJmsConsumer" class="SpringJmsConsumer">
    <constructor-arg ref="connectionFactory" />
    <constructor-arg ref="messageDestination" />
</bean>

Note that connection failures are not managed by this code.

Hassen Bennour
  • 3,885
  • 2
  • 12
  • 20
  • Hassen, Thanks but i have already tried your updated answer multiple times but still ending up with reading new messages only. I have also tried your 1st point but still things are not working. Any other approach ? – Sandip Jangra Mar 06 '18 at 14:25
  • When you connect to AMQ web console did you see the durable subscriber in the list ? – Hassen Bennour Mar 06 '18 at 16:44
  • I think that you have tried my answer before i have updated ?? Because i seen now that my code cannot work because the connectionFactory was set after constructor... i have updated it right now with better code to try. Note that this code does not manage connection failure... can you test last version. I have updated springJmsConsumer definition too – Hassen Bennour Mar 06 '18 at 19:52
  • Thanks bro, this constructor idea worked fine. So what can i do to handle connection failure ? I have putted catch and finally to close connection and also implemented disposable bean to close connection on destroy if connection is not null. Is that fine or i need to do something else to handle connection failure ? because here we are not using jmsTemplate and we are creating connection manually. – Sandip Jangra Mar 07 '18 at 08:59
  • 1
    @SANDIP take a look at http://activemq.apache.org/failover-transport-reference.html and http://activemq.apache.org/redelivery-policy.html – Hassen Bennour Mar 07 '18 at 09:28
  • Thank you so much for your support Hassen :) – Sandip Jangra Mar 07 '18 at 09:34