Sending and receiving messages using Apache ActiveMQ with Spring Boot

Hi,

This is the continuation of my previous post Apache ActiveMQ. We shall see how to send and receive messages with SpringBoot using this ActiveMQ server.

STS Project Setup

Goto File>New>Spring Starter Project. We shall take the project template from Spring.io.

STS project

Start a new SpringProject

Include the JMS dependency

Your new project will be created. I need to add junit4 separately.

Maven dependencies

This is my pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.7.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>org.grassfield</groupId>
	<artifactId>jmsdemo</artifactId>
	<version>1.0</version>
	<name>jmsdemo</name>
	<description>jmsdemo</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
	</dependencies>

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

</project>

Application.properties

Add the following property to Application.properties

activemq.broker-url: tcp://192.168.1.150:61616

If you didn’t see my earlier post, 192.168.1.150 is where my ActiveMQ server is running.

Sender code:

Sender is a simple POJO, that contains the methods to send a message.

Sender.java

package org.grassfield;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;

public class Sender {
	private static final Logger LOGGER = LoggerFactory.getLogger(Sender.class);
	@Autowired
	private JmsTemplate jmsTemplate;
	
	public void send(String message) {
	    LOGGER.info("sending message='{}'", message);
	    jmsTemplate.convertAndSend("myqueue.q", message);
	  }
}

SenderConfig.java

package org.grassfield;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.core.JmsTemplate;

@Configuration
public class SenderConfig {
	Logger logger = LoggerFactory.getLogger(SenderConfig.class);
	@Value("${activemq.broker-url}")
	private String brokerUrl;
	
	public ActiveMQConnectionFactory senderActiveMQConnectionFactory() {
		logger.info("initializing ActiveMQConnectionFactory");
		ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
		logger.info("Setting up broker "+brokerUrl);
		activeMQConnectionFactory.setBrokerURL(brokerUrl);
		return activeMQConnectionFactory;
	}
	
	@Bean
	public CachingConnectionFactory cachingConnectionFactory() {
		return new CachingConnectionFactory(senderActiveMQConnectionFactory());
	}
	
	@Bean
	public JmsTemplate jmsTemplate() {
		return new JmsTemplate(cachingConnectionFactory());
	}
	
	@Bean
	public Sender sender() {
		return new Sender();
	}
}

Receiver Code

This code will receives the message sent by the Sender.java

Receiver.java

package org.grassfield;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;

public class Receiver {
	Logger LOGGER = LoggerFactory.getLogger(Receiver.class);

	  @JmsListener(destination = "myqueue.q")
	  public void receive(String message) {
	    LOGGER.info("received message='{}'", message);
	  }
}

ReceiverConfig.java

package org.grassfield;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;

@Configuration
@EnableJms
public class ReceiverConfig {
	@Value("${activemq.broker-url}")
	private String brokerUrl;
	
	@Bean
	public ActiveMQConnectionFactory receiverActiveMQConnectionFactory() {
		ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
		activeMQConnectionFactory.setBrokerURL(brokerUrl);
	    return activeMQConnectionFactory;
	}
	
	@Bean
	public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
	    factory.setConnectionFactory(receiverActiveMQConnectionFactory());
	    return factory;
	}
	
	@Bean
	public Receiver receiver() {
		return new Receiver();
	}

}

Spring Boot Application

And finally, this is our Spring Boot Application.

package org.grassfield;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JmsdemoApplication {

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

}

Unit test

This is the unit test.

package com.eg;

import org.grassfield.JmsdemoApplication;
import org.grassfield.Sender;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = JmsdemoApplication.class)
class JmsdemoApplicationTest {

	@Autowired
	private Sender sender;

	@Test
	public void testReceive() {
		sender.send("Hello Spring JMS ActiveMQ!");
	}

}

Execution

We shall execute the unit test cases by running the project as a maven build with test goal.

As you see above, the message is sent by the Sender. The same has been received by Receiver.

Queue views – Graphs
Topics view statistics

Hence the message is sent and received.

Thanks Spring JMS ActiveMQ Example -CodeNotFound

Could not write JSON: (was java.lang.NullPointerException)

It was a dirty exception I faced few weeks back. I was trying to to consume a REST API call using postForEntity.

Unfortunately my ID variable was in primitive long instead of Long. I hit with the following error. I had been roaming around without a clue.

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.NullPointerException)

Ref: https://stackoverflow.com/questions/24805043/org-springframework-http-converter-httpmessagenotwritableexception-could-not-wr

I shot this soft white sand beach at Coney Island recently

I shot this soft white sand beach at Coney Island recently

Failed to read auto-increment value from storage engine

One after another – This was a strange exception I received today. I am not sure if this is a bug of JPA or underlying MySQL.


SQL Error: 1467, SQLState: HY000

Failed to read auto-increment value from storage engine

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement] with root cause

java.sql.SQLException: Failed to read auto-increment value from storage engine

I was pretty much surprised, what else!

Server version: 10.1.36-MariaDB

Spring Boot :: (v2.1.7.RELEASE)

I tried this.


ALTER TABLE `feed_item_info` AUTO_INCREMENT =1

It does not help me.

Later –

ALTER TABLE `feed_item_info` ORDER BY `index`

It throws the following warning.

Warning: #1105 ORDER BY ignored as there is a user-defined clustered index in the table ‘feed_item_info’

But it was working.

Ref:https://stackoverflow.com/questions/17690926/failed-to-read-auto-increment-value-from-storage-engine-error-number-1467

I shot this recently, at the seashore which borders Singapore and Malaysia

I shot this recently, at the seashore which borders Singapore and Malaysia

detached entity passed to persist

I am in mid of a crud operation. This exception hit me today, when I updated an entity with @ManyToOne(cascade = CascadeType.ALL) relationship.

org.hibernate.PersistentObjectException: detached entity passed to persist: org.grassfield.feed.entity.Feed

To solve this bidirectional consistency problem, I need to change CascadeType.ALL to CasecaseType.MERGE.

Recently I shot this pretty lady (or mighty gentleman?) at Coney Island. This has no relation with this post anyway.

Recently I shot this pretty lady (or mighty gentleman?) at Coney Island. This has no relation with this post anyway.