Configure a Spring JMS application with Spring Boot and annotation support

In previous posts we learned how to configure a project using Spring JMS. If you check the article introduction to messaging with Spring JMS, you will notice that it is configured using XML. This article will take advantage of the improvements introduced in Spring 4.1 version, and configure a Spring JMS application with Spring Boot using Java config only.

In this example we will also see how easy it can be to configure the project by using Spring Boot.

Before we get started, just note that as usual, you can take a look at the source code of the project used in the examples below.

See the example project at github.

Sections:

  1. Introduction.
  2. The example application.
  3. Setting up the project.
  4. A simple example with JMS listener.
  5. Sending a response to another queue with @SendTo.
  6. Conclusion.

 

1 The example application

The application uses a Client service to send orders to a JMS queue, where a JMS listener will be registered and handle these orders. Once received, the listener will store the order through the Store service:

Spring JMS application with Spring Boot diagram

 

We will use the Order class to create orders:

public class Order implements Serializable {
    private static final long serialVersionUID = -797586847427389162L;
    
    private final String id;
    
    public Order(String id) {
        this.id = id;
    }
    
    public String getId() {
        return id;
    }
}

 

Before moving on to the first example, we will first explore how the project structure is built.

2 Setting up the project

2.1 Configuring pom.xml

The first thing to do is to define the artifact spring-boot-starter-parent as our parent pom.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

 

This parent basically sets several Maven defaults and provides the dependency management for the main dependencies that we will use, like the Spring version (which is 4.1.6).

It is important to note that this parent pom defines the version of many libraries but it does not add any dependency to our project. So don’t worry about getting libraries you won’t use.

The next step is to set the basic dependencies for Spring Boot:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

 

In addition to the core Spring libraries, this dependency will bring the auto configuration functionality of Spring Boot. This will allow the framework to try to automatically set up the configuration based on the dependencies you add.

Finally, we will add the Spring JMS dependency and the ActiveMQ message broker, leaving the whole pom.xml as follows:

<groupId>xpadro.spring</groupId>
<artifactId>jms-boot-javaconfig</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>JMS Spring Boot Javaconfig</name>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <start-class>xpadro.spring.jms.JmsJavaconfigApplication</start-class>
    <java.version>1.8</java.version>
    <amq.version>5.4.2</amq.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-core</artifactId>
        <version>${amq.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.2 Spring Configuration with Java Config

We used @SpringBootApplication instead of the usual @Configuration annotation. This Spring Boot annotation is also annotated with @Configuration. In addition, it sets other configuration like Spring Boot auto configuration:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {

 

The configuration class does not need to define any bean. All the configuration is automatically set by Spring Boot. Regarding the connection factory, Spring Boot will detect that I included the ActiveMQ dependency on the classpath and will start and configure an embedded broker.

@SpringBootApplication
public class JmsJavaconfigApplication {

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

 

If you need to specify a different broker url, you can declare it in the properties. Check ActiveMQ support section for further detail.

It is all set now. We will see how to configure a JMS listener in the example in the next section, since it is configured with an annotation.

3 A simple example with JMS listener

3.1   Sending an order to a JMS queue

The ClientService class is responsible for sending a new order to the JMS queue. In order to accomplish this, it uses a JmsTemplate:

@Service
public class ClientServiceImpl implements ClientService {
    private static final String SIMPLE_QUEUE = "simple.queue";
    
    private final JmsTemplate jmsTemplate;
    
    @Autowired
    public ClientServiceImpl(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }
    
    @Override
    public void addOrder(Order order) {
        jmsTemplate.convertAndSend(SIMPLE_QUEUE, order);
    }
}

 

Here, we use a JmsTemplate to convert our Order instance and send it to the JMS queue. If you prefer to directly send a message through the send message, you can instead use the new JmsMessagingTemplate. This is preferable since it uses the more standardized Message class.

3.2 Receiving an order sent to the JMS queue

Registering a JMS listener to a JMS listener container is as simple as adding the @JmsListener annotation to the method we want to use. This will create a JMS listener container under the covers that will receive messages sent to the specified queue and delegate them to our listener class:

@Component
public class SimpleListener {
    private final StoreService storeService;
    
    @Autowired
    public SimpleListener(StoreService storeService) {
        this.storeService = storeService;
    }
    
    @JmsListener(destination = "simple.queue")
    public void receiveOrder(Order order) {
        storeService.registerOrder(order);
    }
}

 

The StoreService receives the order and saves it to a list of received orders:

@Service
public class StoreServiceImpl implements StoreService {
    private final List<Order> receivedOrders = new ArrayList<>();
    
    @Override
    public void registerOrder(Order order) {
        this.receivedOrders.add(order);
    }
    
    @Override
    public Optional<Order> getReceivedOrder(String id) {
        return receivedOrders.stream().filter(o -> o.getId().equals(id)).findFirst();
    }
}

3.3 Testing the application

Now let’s add a test to check if we did everything correctly:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = JmsJavaconfigApplication.class)
public class SimpleListenerTest {
    
    @Autowired
    private ClientService clientService;
    
    @Autowired
    private StoreService storeService;
    
    @Test
    public void sendSimpleMessage() {
        clientService.addOrder(new Order("order1"));
        
        Optional<Order> storedOrder = storeService.getReceivedOrder("order1");
        Assert.assertTrue(storedOrder.isPresent());
        Assert.assertEquals("order1", storedOrder.get().getId());
    }
}

 

4 Sending a response to another queue with @SendTo

Another addition to Spring JMS is the @SendTo annotation. This annotation allows a listener to send a message to another queue. For example, the following listener receives an order from the “in.queue” and after storing the order, sends a confirmation to the “out.queue”.

@JmsListener(destination = "in.queue")
@SendTo("out.queue")
public String receiveOrder(Order order) {
    storeService.registerOrder(order);
    return order.getId();
}

 

There, we have another listener registered that will process this confirmation id:

@JmsListener(destination = "out.queue")
public void receiveOrder(String orderId) {
    registerService.registerOrderId(orderId);
}

 

5 Conclusion

With annotation support, it is now much easier to configure a Spring JMS application, taking advantage of asynchronous message retrieval using annotated JMS listeners.

I’m publishing my new posts on Google plus and Twitter. Follow me if you want to be updated with new content.