sqs-spring-boot-localstack

Creating Queues (LocalStack)

Start LocalStack

Create Queues

awslocal sqs create-queue --queue-name outcoming_messages_queue --region us-east-1 --endpoint-url http://localhost:4566
awslocal sqs create-queue --queue-name incoming_messages_queue --region us-east-1 --endpoint-url http://localhost:4566

awslocal sqs send-message --message-body "test-sending-01" --queue-url "http://localhost:4566/000000000000/incoming_messages_queue" --region us-east-1 --endpoint-url http://localhost:4566

Project Structure

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>3.1.6</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<groupId>zjc.examples</groupId>
	<artifactId>sqs</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<description>Demo project for SQS, Spring Boot and LocalStack</description>

	<properties>
		<java.version>17</java.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jms</artifactId>
			<version>5.3.3</version>
		</dependency>

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.12.1</version>
		</dependency>

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.12.1</version>
		</dependency>

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.12.1</version>
		</dependency>

		<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>aws-java-sdk</artifactId>
			<version>1.11.939</version>
		</dependency>

		<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>amazon-sqs-java-messaging-lib</artifactId>
			<version>1.0.8</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<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

outcoming.queue.name=outcoming_messages_queue
incoming.queue.name=incoming_messages_queue

AppConfig.java

package zjc.examples.sqs.config;

import com.amazon.sqs.javamessaging.ProviderConfiguration;
import com.amazon.sqs.javamessaging.SQSConnectionFactory;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.sqs.*;
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;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.jms.support.destination.DynamicDestinationResolver;
import zjc.examples.sqs.domain.MessageConsumer;
import zjc.examples.sqs.domain.MessageProducer;
import zjc.examples.sqs.receivers.JMSMessageListener;
import zjc.examples.sqs.senders.JMSMessageSender;

import javax.jms.Session;

@Configuration
@EnableJms
public class AppConfig {

    @Value("${outcoming.queue.name}")
    private String outcomingQueueName;

    @Bean
    public SQSConnectionFactory connectionFactory() {
        AmazonSQSAsync build = AmazonSQSAsyncClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("xxx", "yyy")))
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://localhost:4566", "us-east-1"))
                .build();

        return new SQSConnectionFactory(new ProviderConfiguration(), build);
    }

    @Bean
    public MessageConverter mappingJackson2MessageConverter() {
        MappingJackson2MessageConverter jackson2MessageConverter = new MappingJackson2MessageConverter();
        jackson2MessageConverter.setTargetType(MessageType.TEXT);
        jackson2MessageConverter.setTypeIdPropertyName("_type");
        return jackson2MessageConverter;
    }

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(SQSConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setDestinationResolver(new DynamicDestinationResolver());
        factory.setConcurrency("3-10");
        factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
        return factory;
    }

    @Bean
    public JmsTemplate jmsTemplate(SQSConnectionFactory connectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
        jmsTemplate.setMessageConverter(mappingJackson2MessageConverter());
        return jmsTemplate;
    }

    @Bean
    public MessageProducer messageProducer(JmsTemplate jmsTemplate) {
        return new MessageProducer(new JMSMessageSender(jmsTemplate, outcomingQueueName));
    }

    @Bean
    public MessageConsumer messageConsumer() {
        return new MessageConsumer();
    }

    @Bean
    public JMSMessageListener jmsMessageListener() {
        return new JMSMessageListener(messageConsumer());
    }
}
@Bean
public SQSConnectionFactory connectionFactory() {
    AmazonSQSAsync build = AmazonSQSAsyncClientBuilder.standard()
            .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("xxx", "yyy")))
            .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://localhost:4566", "us-east-1"))
            .build();

    return new SQSConnectionFactory(new ProviderConfiguration(), build);
}

MessageController.java

package zjc.examples.sqs.webaccess;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import zjc.examples.sqs.domain.Message;
import zjc.examples.sqs.domain.MessageConsumer;
import zjc.examples.sqs.domain.MessageProducer;

@RestController
public class MessageController {

    private final MessageConsumer messageConsumer;
    private final MessageProducer messageProducer;

    public MessageController(MessageConsumer messageConsumer, MessageProducer messageProducer) {
        this.messageConsumer = messageConsumer;
        this.messageProducer = messageProducer;
    }

    @GetMapping("/last-message")
    Message getLastMessage() {
        return messageConsumer.getLastReceivedMessage();
    }

    @PostMapping("/send-message/{text}")
    String sendMessage(@PathVariable String text) {
        messageProducer.sendMessage(text);
        return "message " + text + " sent";
    }
}

Send Message

Postman:

https://localhost.localstack.cloud:4566:

Mark