Ikthiander Ikthiander - 1 month ago 20
Java Question

How to use @JmsListener for topic in Spring 4

I am using Spring 4.3.3 and I am trying to listen to a message where the destination type is topic.

I can achieve it in xml:

<jms:listener-container connection-factory="connectionFactory"
destination-type="topic"
message-converter="jackson2MessageConverter">
<jms:listener destination="test.topic" ref="jmsTopicMessageListener1" method="receiveMessage"/>
<jms:listener destination="test.topic" ref="jmsTopicMessageListener2" method="receiveMessage"/>
</jms:listener-container>


But I want to use @JmsListener. at the moment it is only working for queue destination like this:

@JmsListener(destination = "mailbox", containerFactory = "jmsListenerContainerFactory")
public void receiveMessage(DataObject dataObject) {
System.out.println("Received <" + dataObject.getName() + ">");
}


How do I listen to a topic with this annotation @JmsListener?

Thanks in advance.

UPDATE:

I have tried this in the config:

<bean id="topicJmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<property name="destinationResolver" ref="jmsDestResolver"/>
<property name="concurrency" value="3-10"/>
<property name="messageConverter" ref="jackson2MessageConverter"/>
<property name="pubSubDomain" value="true"/>
</bean>


then this:

@JmsListener(destination = "test.topic", containerFactory = "topicJmsListenerContainerFactory")
public void receiveMessage(DataObject dataObject) {
System.out.println("Received <" + dataObject.getName() + ">" + 1);
}


I can get the values now but i get duplicates.

Answer

Since you have fixed your config (you are receiving messages that is) the only problem to solve is the fact that you are receiving duplicate messages. This is due to this piece of your config:

<property name="concurrency" value="3-10"/>

If you check the javadoc of DefaultMessageListenerContainer (which JmsListenerContainerFactory extends), you will find that:

...for a topic, you will typically stick with the default number of 1 consumer, otherwise you'd receive the same message multiple times on the same node.

This is due to the architecture of the topic itself, multiple consumers will be treated as multiple subscribers who need to receive each message that was sent.

On the solution side:

You might need to reachitecture your code a bit. But most cases changing from Topic to VirtualTopic does the job. With VirtualTopics you can get "Queue-like" behaviour on the consumer side, meaning that messages will get consumed by a consumer, so multiple consumers cannot see the same message twice.

The other option of course is shifting the parallel processing workload to some other place and use a single consumer at the JMS entrypoint. Depending on the task at hand it might be a sufficient solution too.