I've been away from the Java world for a while, but I recently helped an old friend with a payment gateway project that required integration with certain AWS services. Unfortunately, the DevOps team didn't provide him with a test account in time to meet his deadline. To solve this issue, we decided to try LocalStack and we were pleasantly surprised by how easy it was to set up and use.
Our mission was to use SQS for payment processing logic to publish and consume messages asynchronously within the payment journey.
In this post I will show you how we configured, used and tested the code with LocalStack.
Configuration
First of all, make sure you have Docker installed.
The project uses Spring Boot 3 with Maven, so the required dependencies are:
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter</artifactId>
</dependency>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-starter-sqs</artifactId>
</dependency>
A Spring configuration class was created to set things up
@Configuration
@Slf4j // Using Lombok
public class AwsSqsLocalstackConfiguration {
@Value("${spring.cloud.aws.credentials.access-key}")
protected String awsAccessKey;
@Value("${spring.cloud.aws.credentials.secret-key}")
protected String awsSecretKey;
@Value("${spring.cloud.aws.region.static:us-east-1}")
protected String awsRegion;
protected AwsCredentialsProvider amazonAWSCredentialsProvider() {
return StaticCredentialsProvider.create(AwsBasicCredentials.create(awsAccessKey, awsSecretKey));
}
static LocalStackContainer localStack =
new LocalStackContainer(DockerImageName.parse("localstack/localstack"));
@Bean
@Primary
SqsAsyncClient sqsClient() {
//startLocalStack();
localStack.start();
return SqsAsyncClient.builder()
.endpointOverride(URI.create(localStack.getEndpointOverride(SQS).toString()))
.credentialsProvider(amazonAWSCredentialsProvider()).region(Region.of(awsRegion)).build();
}
@Bean
SqsTemplate sqsTemplate(SqsAsyncClient sqsAsyncClient) {
return SqsTemplate.builder().sqsAsyncClient(sqsAsyncClient).build();
}
// Telling SQSListener to use object mapper for obj serialization.
@Bean
SqsListenerConfigurer configurer(ObjectMapper objectMapper) {
return registrar -> registrar.setObjectMapper(objectMapper);
}
}
To save time and initialize LocalStack with queues, simply add the following code to the configuration class above. Otherwise, you can execute shell commands directly in the container to add it manually.
@Autowired
private LocalstackQueues localstackQeues;
private void startLocalStack() {
localStack.start();
// Creating the queues
localstackQeues.queues().forEach(queue -> {
try {
localStack.execInContainer("awslocal", "sqs", "create-queue", "--queue-name", queue);
log.info("Queue {} created", queue);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
log.info("LOCALSTACK RUNNING AT {}", localStack.getEndpointOverride(SQS).toString());
}
// Add the queues in your application.yml e.g.
// localstack:
// queues:
// - my.queue.1
// - my.queue.2
@Component
@ConfigurationProperties(prefix = "localstack.queues")
record LocalstackQueues(List<String> queues) {
}
Ensure that LocalStack's Docker container is running without any errors during initialization by executing your Spring Boot application.
Stay tuned for part 2, where we'll write both an SQS Publisher and an SQS Listener. And make no mistake, part 3 will show you how to write tests with LocalStack.
Top comments (0)