Mixing pub/sub and point-to-point in Spring Boot
Tags: jms, springboot, queue, topic
Published: 2020-07-09 21:43:29

---
title: Mixing pub/sub and point-to-point in Spring Boot
published: true
description: 
tags: jms, springboot, queue, topic
//cover_image: https://direct_url_to_image.jpg
---

# 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:
```java
    jmsTemplate.convertAndSend("MY.QUEUE", message);
```

To receive messages from queue, create a message listener:
```java
    @JmsListener(destination = "MY.QUEUE")
    public void receive(String message) {
        ...
    }
```

# 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:

```properties
    spring.jms.pub-sub-domain=true
```

But there is a drawback with this solution. As the `pub-sub-domain` property is global, all `JmsTemplate`s and `JmsListener`s 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:

```java
@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;
    }

}
```

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

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

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

## 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.

```java
@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);
            }
        };
    }

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

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

jmsTemplate.convertAndSend("MY.TOPIC", message);
```

# 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.