I am creating a spring boot application to log messages on a SQS queue, I am getting a security token error when starting the application. I am using spring boot version 2.7.9, the application is written in kotlin and is running on a windows platform. I have a docker compose file to start Localstack and can create topics, queues and subscriptions. My expectation is that when I send a message to the sqs queue in my bash shell that the application logs the message. I don't know if the docker compose file needs tweaking or if my spring boot configuration is messed up. Please help point out what I am doing wrong or have missed, thank you! I am also trying to setup a test using a test container.
error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleMessageListenerContainer' defined in class path resource [org/springframework/cloud/aws/messaging/config/annotation/SqsConfiguration.class]: Invocation of init method failed; nested exception is com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 4cb154fc-3a61-58ca-aeea-086c2e52e235; Proxy: null)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.25.jar:5.3.25]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.25.jar:5.3.25]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.25.jar:5.3.25]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.9.jar:2.7.9]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.9.jar:2.7.9]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.9.jar:2.7.9]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.9.jar:2.7.9]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.9.jar:2.7.9]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.9.jar:2.7.9]
at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:20) ~[main/:na]
AWS cli configure used to create creds
aws configure set aws_access_key_id "dummy" --profile test-profile
aws configure set aws_secret_access_key "dummy" --profile test-profile
aws configure set region "us-east-1" --profile test-profile
aws configure set output "table" --profile test-profile
AWS topic/queue created with the following commands in my git bash terminal
aws sns create-topic --endpoint-url=http://localhost:4566 --name test-topic --region us-east-1 --profile test-profile --output table | cat
aws sqs create-queue --endpoint-url=http://localhost:4566 --queue-name test-queue --profile test-profile --region us-east-1 --output table | cat
aws sns subscribe --endpoint-url http://localhost:4566 --topic-arn arn:aws:sns:us-east-1:000000000000:test-topic --profile test-profile --protocol sqs --notification-endpoint arn:aws:sns:us-east-1:000000000000:test-queue --output table | cat
aws sns list-subscriptions --endpoint-url=http://localhost:4566
aws sns publish --endpoint-url=http://localhost:4566 --topic-arn arn:aws:sns:us-east-1:000000000000:test-topic --message "Hello World" --profile test-profile --region us-east-1 --output json | cat
Did this to to check in bash termial I could see the message:
aws sqs receive-message --endpoint-url=http://localhost:4566 --queue-url http://localhost:4566/000000000000/test-queue --profile test-profile --region us-east-1 --output json | cat
docker file:
version: '3.0'
services:
localstack:
image: localstack/localstack:latest
environment:
- SERVICES=sqs,sns
- AWS_DEFAULT_REGION=us-east-1
- EDGE_PORT=4566
ports:
- '4566-4597:4566-4597'
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
build.gradle
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.7.9"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE")
implementation("org.springframework.cloud:spring-cloud-starter-aws-messaging:2.2.6.RELEASE")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
Spring boot cofiguraion file, I create an AWS test-profile with aws_access_key_id and aws_secret_access_key set to 'dummy'
package com.example.demo
import com.amazonaws.auth.AWSCredentials
import com.amazonaws.auth.AWSStaticCredentialsProvider
import com.amazonaws.auth.BasicAWSCredentials
import com.amazonaws.regions.Regions
import com.amazonaws.services.sqs.AmazonSQSAsync
import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.cloud.aws.messaging.config.SimpleMessageListenerContainerFactory
import org.springframework.cloud.aws.messaging.config.annotation.EnableSqs
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import javax.annotation.PostConstruct
@Configuration
@EnableSqs
class SqsConfiguration {
private val logger: Logger = LoggerFactory.getLogger(SqsConfiguration::class.java)
@Bean
fun credentials(): AWSCredentials {
return BasicAWSCredentials("dummy", "dummy")
}
@Bean
fun queueMessagingTemplate(): QueueMessagingTemplate? {
return QueueMessagingTemplate(amazonSQSAsync())
}
fun amazonSQSAsync(): AmazonSQSAsync {
return AmazonSQSAsyncClientBuilder.standard()
.withRegion(Regions.US_EAST_1)
.withCredentials(AWSStaticCredentialsProvider(credentials()))
.build()
}
@Bean
fun simpleMessageListenerContainerFactory(): SimpleMessageListenerContainerFactory? {
val msgListenerContainerFactory = SimpleMessageListenerContainerFactory()
msgListenerContainerFactory.setAmazonSqs(amazonSQSAsync())
return msgListenerContainerFactory
}
@PostConstruct
fun init() {
logger.info("SqsConfiguration created")
}
}
The consumer class is:
package com.example.demo
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.cloud.aws.messaging.listener.annotation.SqsListener
import org.springframework.stereotype.Component
import javax.annotation.PostConstruct
@Component
class Consumer {
private val logger: Logger = LoggerFactory.getLogger(Consumer::class.java)
@SqsListener("test-queue")
fun receiveMessage(message: String) {
logger.info("Received message: {}", message)
}
@PostConstruct
fun init() {
logger.info("Consumer created")
}
}
Finally the boot strap class is:
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication(
exclude =
[
org.springframework.cloud.aws.autoconfigure.context.ContextInstanceDataAutoConfiguration::class,
org.springframework.cloud.aws.autoconfigure.context.ContextStackAutoConfiguration::class,
org.springframework.cloud.aws.autoconfigure.context.ContextRegionProviderAutoConfiguration::class
]
)
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}