Connect a Micronaut JMS Application to an AWS SQS Queue

Learn how to connect JMS Application to an AWS SQS Queue

Authors: Slavko Bodvanski

Micronaut Version: 3.9.2

1. Getting Started

In this guide, we will create a Micronaut application written in Groovy.

2. What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.8 or greater installed with JAVA_HOME configured appropriately

  • An AWS account with:

    • An IAM user with enough permissions to create and manage a queue instances in SQS.

    • The AWS CLI configured to use the IAM user above.

3. Amazon Web Services (AWS)

If you don’t have one already, create an AWS Account.

3.1. AWS CLI

Follow the AWS documentation for installing or updating the latest version of the AWS CLI.

3.2. Administrator IAM user

Instead of using your AWS root account, it is recommended that you use an IAM administrative user. If you don’t have one already, follow the steps below to create one:

aws iam create-group --group-name Administrators
aws iam create-user --user-name Administrator
aws iam add-user-to-group --user-name Administrator --group-name Administrators
aws iam attach-group-policy --group-name Administrators --policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AdministratorAccess`].{ARN:Arn}' --output text)
aws iam create-access-key --user-name Administrator

Then, run aws configure to configure your AWS CLI to use the Administrator IAM user just created.

4. Solution

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

5. Writing the Application

Create an application using the Micronaut Command Line Interface or with Micronaut Launch.

mn create-app example.micronaut.micronautguide \
    --features=jms-sqs \
    --build=gradle
    --lang=groovy
If you don’t specify the --build argument, Gradle is used as the build tool.
If you don’t specify the --lang argument, Java is used as the language.

The previous command creates a Micronaut application with the default package example.micronaut in a directory named micronautguide.

If you use Micronaut Launch, select Micronaut Application as application type and add jms-sqs features.

If you have an existing Micronaut application and want to add the functionality described here, you can view the dependency and configuration changes from the specified features and apply those changes to your application.

6. Create an application

Let’s create a set of components that will use the Micronaut JMS to send and receive messages from AWS SQS

Amazon SQS is a reliable, highly-scalable hosted queue for storing messages as they travel between applications or microservices. Amazon SQS moves data between distributed application components and helps you decouple these components.

6.1. Creating a JMS Producer

Create a JMS Producer interface.

src/main/groovy/example/micronaut/DemoProducer.groovy
package example.micronaut

import groovy.transform.CompileStatic
import io.micronaut.jms.annotations.JMSProducer
import io.micronaut.jms.annotations.Queue
import io.micronaut.messaging.annotation.MessageBody

import static io.micronaut.jms.sqs.configuration.SqsConfiguration.CONNECTION_FACTORY_BEAN_NAME

@CompileStatic
@JMSProducer(CONNECTION_FACTORY_BEAN_NAME) (1)
interface DemoProducer {

    @Queue("demo_queue")  (2)
    void send(@MessageBody String body)  (3)
}
1 The JMSProducer annotation defines this interface as a client that sends messages.
2 The @Queue annotation indicates which queue the message should be published to.
3 The send method accepts a single parameter which is the payload of a message.

6.2. Creating a JMS Consumer

Create a JMS Consumer class.

src/main/groovy/example/micronaut/DemoConsumer.groovy
package example.micronaut

import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import io.micronaut.jms.annotations.JMSListener
import io.micronaut.jms.annotations.Queue
import io.micronaut.messaging.annotation.MessageBody

import java.util.concurrent.atomic.AtomicInteger

import static io.micronaut.jms.sqs.configuration.SqsConfiguration.CONNECTION_FACTORY_BEAN_NAME

@CompileStatic
@Slf4j('LOGGER')
@JMSListener(CONNECTION_FACTORY_BEAN_NAME)  (1)
class DemoConsumer {

    private final AtomicInteger messageCount = new AtomicInteger(0);

    @Queue(value = "demo_queue", concurrency = "1-3")  (2)
    void receive(@MessageBody String body) {  (3)
        LOGGER.info("Message consumed: {}", body);
        messageCount.incrementAndGet()
    }

    int getMessageCount() {
        messageCount.intValue();
    }
}
1 The @JMSListener defines the bean as a message listener.
2 The @Queue annotation indicates which queue to subscribe to.
3 The receive method accepts a single parameter which is the payload of a message.

6.3. Configure a SQS client factory

Additionally, you’ll need to configure an instance of com.amazonaws.services.sqs.AmazonSQS as a bean for AWS authentication, for example:

src/main/groovy/example/micronaut/SqsClientFactory.groovy
package example.micronaut

import com.amazonaws.regions.Regions
import com.amazonaws.services.sqs.AmazonSQS
import com.amazonaws.services.sqs.AmazonSQSClientBuilder
import groovy.transform.CompileStatic
import io.micronaut.aws.sdk.v1.EnvironmentAWSCredentialsProvider
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Requires
import io.micronaut.context.env.Environment
import jakarta.inject.Singleton


@CompileStatic
@Factory  (1)
@Requires(notEnv = Environment.TEST)
class SqsClientFactory {

    @Singleton
    AmazonSQS sqsClient(Environment environment) {  (2)
        AmazonSQSClientBuilder
                .standard()
                .withRegion(Regions.US_EAST_1)  (3)
                .withCredentials(new EnvironmentAWSCredentialsProvider(environment))  (4)
                .build()
    }
}
1 A class annotated with the @Factory annotated is a factory. It provides one or more methods annotated with a bean scope annotation (e.g. @Singleton). Read more about Bean factories.
2 Configures com.amazonaws.services.sqs.AmazonSQS bean as a singleton instance.
3 Sets the region to be used by the client. This will be used to determine both the service endpoint (eg: https://sns.us-east-1.amazonaws.com) and signing region (eg: us-east-1) for requests.
4 Sets the com.amazonaws.auth.AWSCredentialsProvider used by the client. In this example sets the provider that reads from the io.micronaut.context.env.Environment.

6.4. Creating a Controller

Let’s create a Controller with an endpoint that we will call to verify that message has been sent by the JMS Producer (DemoProducer) and then finally received and consumed by the JMS Consumer (DemoConsumer).

src/main/groovy/example/micronaut/DemoController.groovy
package example.micronaut

import groovy.transform.CompileStatic
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.annotation.Status

@CompileStatic
@Controller  (1)
class DemoController {

    private final DemoProducer demoProducer

    DemoController(DemoProducer demoProducer) {  (2)
        this.demoProducer = demoProducer
    }

    @Post("/demo")  (3)
    @Status(HttpStatus.NO_CONTENT)
    void publishDemoMessages() {
        demoProducer.send("Demo message body")  (4)
    }
}
1 The class is defined as a controller with the @Controller annotation mapped to the path /.
2 Injects DemoProducer bean.
3 Maps a GET request to /demo path, which attempts to publish a message to a SQS queue instance.
4 Calls send method on DemoProducer instances providing the message payload.

7. Creating a queue instance in Amazon Simple Queue Service (Amazon SQS)

You will create a queue with the AWS CLI. See the AWS CLI sqs command for more information.

7.1. Create a queue instance

aws sqs create-queue --queue-name demo_queue

Copy and save the response of the command. You will need the QueueUrl to delete the queue after you finish with it.

8. Update the Application configuration to use AWS

8.1. Add the AWS IAM user access key and secret key placeholders to the Application Configuration

src/main/resources/application.yml
micronaut:
  application:
    name: demo
  jms:
    sqs:
      enabled: true
+ aws:
+  access-key-id: ${AWS_ACCESS_KEY_ID}
+  secret-key: ${AWS_SECRET_KEY}

9. Running the Application

With almost everything in place, you can start the application and try it out. First, set environment variables to configure the queue connection. Then you can start the app.

Create environment variables for AWS_ACCESS_KEY_ID, and AWS_SECRET_KEY, which will be used in the Micronaut app’s application.yml:

export AWS_ACCESS_KEY_ID=<the access key from the AWS configuratipn step>
export AWS_SECRET_KEY=<the secret key from the AWS configuratipn step>
Window System
Command Prompt

Change 'export' to 'set'

Example: set AWS_ACCESS_KEY_ID=aws_access_key

PowerShell

Change 'export ' to '$' and use quotes around the value

Example: $AWS_ACCESS_KEY_ID="aws_access_key"

To run the application, use the ./gradlew run command, which starts the application on port 8080.

You can test the application in a web browser or with cURL.

Run from a terminal window to publish and consume a message:

curl "http://localhost:8080/demo"

9.1. Stopping the Instance and cleaning up

Once you are done with this guide, you can stop/delete the AWS resources created to avoid incurring unnecessary charges.

aws sqs delete-queue --queue-url <QUEUE_URL>

Replace the <QUEUE_URL> placeholder with a queue URL value returned from the create-queue command.

10. Next steps

Explore more features with Micronaut Guides.

Read more about Micronaut JMS.