16

I am trying to create example for publish-subscribe based on @JmsListener annotation: https://github.com/lkrnac/book-eiws-code-samples/tree/master/05-jms/0515-publish-subscribe

Relevant code snippets:

@Slf4j
@SpringBootApplication
@EnableScheduling
public class JmsPublishSubscribeApplication {

    public static void main(String[] args) throws InterruptedException {
        SpringApplication.run(JmsPublishSubscribeApplication.class, args);
    }

    @Bean
    public ActiveMQTopic simpleTopic() {
        return new ActiveMQTopic("simpleTopic");
    }

}

@Component
public class SimpleMessageListener1 {

    @JmsListener(destination = "simpleTopic")
    public void readMessage(String message) {
      //....
    }

}

@Component
public class SimpleMessageListener2 {

    @JmsListener(destination = "simpleTopic")
    public void readMessage(String message) {
      //....
    }

}

The problem is that is get this behaviour:

2015-05-17 20:07:04.985  INFO 22983 --- [pool-1-thread-1] n.l.b.e.chapter05.SimpleMessageSender    : Sending message: simple message
2015-05-17 20:07:05.070  INFO 22983 --- [enerContainer-1] n.l.b.e.c.JmsPublishSubscribeApplication : Message Received: simple message via listener 2
2015-05-17 20:07:05.975  INFO 22983 --- [pool-1-thread-1] n.l.b.e.chapter05.SimpleMessageSender    : Sending message: simple message
2015-05-17 20:07:05.986  INFO 22983 --- [enerContainer-1] n.l.b.e.c.JmsPublishSubscribeApplication : Message Received: simple message via listener 1
2015-05-17 20:07:06.975  INFO 22983 --- [pool-1-thread-1] n.l.b.e.chapter05.SimpleMessageSender    : Sending message: simple message
2015-05-17 20:07:06.987  INFO 22983 --- [enerContainer-1] n.l.b.e.c.JmsPublishSubscribeApplication : Message Received: simple message via listener 2
2015-05-17 20:07:07.975  INFO 22983 --- [pool-1-thread-1] n.l.b.e.chapter05.SimpleMessageSender    : Sending message: simple message
2015-05-17 20:07:07.994  INFO 22983 --- [enerContainer-1] n.l.b.e.c.JmsPublishSubscribeApplication : Message Received: simple message via listener 1

But each message should be consumed by both listeners by definition of topics. What am I missing?

Abdullah Khan
  • 12,010
  • 6
  • 65
  • 78
luboskrnac
  • 23,973
  • 10
  • 81
  • 92
  • 2
    Add an `application.properties` and set `spring.jms.pub-sub-domain` to `true`. – M. Deinum May 17 '15 at 20:10
  • Can confirm that this flag works. Strange that it doesn't work when you do it programatically: jmsTemplate.setPubSubDomain(true); – luboskrnac May 18 '15 at 06:32
  • @M.Deinum Don't you want to create an answer to earn some points? – luboskrnac May 18 '15 at 06:33
  • 1
    It also works programmatically but not with a `JmsTemplate` as that isn't used by `@JmsListener`. You would have to register a `DefaultMessageListenerContainer` named `jmsListenerContainer` and set that property to `true`. However it is easier to simply add that configuration parameter. – M. Deinum May 18 '15 at 06:42

2 Answers2

38

When using a @JmsListener it uses a DefaultMessageListenerContainer which extends JmsDestinationAccessor which by default has the pubSubDomain set to false. When this property is false it is operating on a queue. If you want to use topics you have to set this properties value to true.

As you are using Spring Boot you can quite easily set this property to true by adding the spring.jms.pub-sub-domain property to the application.properties and set it to true.

spring.jms.pub-sub-domain=true

When using a @JmsListener it is looking for a jmsListenerContainerFactory named bean, if that isn't available a default one is expected. You can also include your own bean and programmatically set this property yo true.

@Bean
public DefaultMessageListenerContainer jmsListenerContainerFactory() {
    DefaultJmsListenerContainerFactory dmlc = new DefaultJmsListenerContainerFactory();
    dmlc.setPubSubDomain(true);
    // Other configuration here
    return dmlc;
}

This would of course also work but would be more work, more information on this can be found in the documentation of the @EnableJms annotation.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • @M. Deinum your advice doesn;t work for me but advice of **ldz** works – gstackoverflow Aug 17 '17 at 23:31
  • Bean named 'jmsListenerContainerFactory' is expected to be of type 'org.springframework.jms.config.JmsListenerContainerFactory' but was actually of type 'org.springframework.jms.listener.DefaultMessageListenerContainer' – gstackoverflow Aug 18 '17 at 06:41
  • yes, it should be `DefaultJmsListenerContainerFactory` not just `DefaultJmsListenerContainer` – havryliuk Aug 18 '22 at 08:16
10

Switching the default destination type of a @JmsListener from Queue to Topic can be done completely in Java without modifying the properties or using XML.

The Spring guide contains an example for customizing the default settings provided by DefaultMessageListenerContainer.

It requires defining a custom bean like follows:

@Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
                                                DefaultJmsListenerContainerFactoryConfigurer configurer) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    // This provides all boot's default to this factory, including the message converter
    configurer.configure(factory, connectionFactory);
    // You could still override some of Boot's default if necessary.
    factory.setPubSubDomain(true);
    return factory;
}

This can then be used in the @JmsListener annotated method:

@JmsListener(destination = "mailbox", containerFactory = "myFactory")
public void receiveMessage(Email email) {
    // implementation
}
ldz
  • 2,217
  • 16
  • 21
  • I want to have the parameters destination and containerFactory as non constant fields. How can I achieve that using annotations? Please help – Dhiraj Gandhi Feb 06 '19 at 14:46
  • 2
    @DhirajGandhi you can do something like this: `@JmsListener(destination = "${async.jms.queue.name}", ...` – FelixM Feb 19 '19 at 16:09
  • I prefer this solution in case you want to listen both to queues and topics. – edgraaff Jul 14 '22 at 15:25