DEV Community

Eduardo Issao Ito
Eduardo Issao Ito

Posted on • Edited on

Mixing pub/sub and point-to-point in Spring Boot

Using JMS point-to-point model (queues)

Using JMS queues in Spring Boot is easy.

Just create a JmsTemplate object and use it to send a message to a queue:

    jmsTemplate.convertAndSend("MY.QUEUE", message);
Enter fullscreen mode Exit fullscreen mode

To receive messages from queue, create a message listener:

    @JmsListener(destination = "MY.QUEUE")
    public void receive(String message) {
        ...
    }
Enter fullscreen mode Exit fullscreen mode

Using JMS pub/sub model (topics)

To use pub/sub model the code is the same as above, you just need to change MY.QUEUE to MY.TOPIC and add the following to application.properties file:

    spring.jms.pub-sub-domain=true
Enter fullscreen mode Exit fullscreen mode

But there is a drawback with this solution. As the pub-sub-domain property is global, all JmsTemplates and JmsListeners will operate with the pub/sub model. You can't use queues and topics in the same application...

Mixing queues and topics

One solution to use both queues and topics in the same Spring Boot application follows.

Subscriber

On the subscriber side you need to create distinct connection factories for queues and topics, setting pubSubDomain property accordingly:

@Configuration
public class JmsConfig {

    @Bean
    public JmsListenerContainerFactory<?> queueConnectionFactory(ConnectionFactory connectionFactory,
                                                                 DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        factory.setPubSubDomain(false);
        return factory;
    }

    @Bean
    public JmsListenerContainerFactory<?> topicConnectionFactory(ConnectionFactory connectionFactory,
                                                                 DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        factory.setPubSubDomain(true);
        return factory;
    }

}
Enter fullscreen mode Exit fullscreen mode

In JmsListener annotations you need to specify which factory will be used:

    @JmsListener(destination = "MY.QUEUE", containerFactory = "queueConnectionFactory")
    public void receiveFromQueue(String text) {
        ...
    }

    @JmsListener(destination = "MY.TOPIC", containerFactory = "topicConnectionFactory")
    public void receiveFromTopic(String text) {
        ...
    }
Enter fullscreen mode Exit fullscreen mode

Publisher

On the publisher side, the solution is a bit more trickier. For this to work, we assume all topics must have a name ending with ".TOPIC" and we configure a DynamicDestinationResolver that sets the pubSubDomain on the destination according to this convention.

@Configuration
public class JmsConfig {

    @Bean
    public DynamicDestinationResolver destinationResolver() {
        return new DynamicDestinationResolver() {
            @Override
            public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
                pubSubDomain = destinationName.endsWith(".TOPIC");
                return super.resolveDestinationName(session, destinationName, pubSubDomain);
            }
        };
    }

}
Enter fullscreen mode Exit fullscreen mode

The same JmsTemplate can be used for both queues and topics because the destination resolver will configure the destination appropriately based on destination name:

jmsTemplate.convertAndSend("MY.QUEUE", message);

jmsTemplate.convertAndSend("MY.TOPIC", message);
Enter fullscreen mode Exit fullscreen mode

Source code

The source code of the complete example is at https://github.com/adzubla/ibm-mq-pubsub

That repository contains a branch named "pubsub-only" with a simpler code that works in pub/sub mode only.

Top comments (2)

Collapse
 
twopelu profile image
Dani P. • Edited

Thanks for the article. Just a question: do you know why producers don't work with the same solutions as consumers? I have been searching for an "elegant" solution for some time, but it seems the only way to make it work is using a workaround... JmsTemplate has some glitches :-(

Collapse
 
adzubla profile image
Eduardo Issao Ito

The architecture of the Spring for JMS is quite simple for basic scenarios, but it gets more convoluted for more complex cases...

Besides, I had no problems using JmsTemplate in quite large applications.