These tutorials target Micronaut Framework 3. Read, Guides for Micronaut Framework 4.

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 Java.

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,graalvm \
    --build=gradle
    --lang=java
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, and graalvm 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/java/example/micronaut/DemoProducer.java
package example.micronaut;

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;

@JMSProducer(CONNECTION_FACTORY_BEAN_NAME) (1)
public 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/java/example/micronaut/DemoConsumer.java
package example.micronaut;

import io.micronaut.jms.annotations.JMSListener;
import io.micronaut.jms.annotations.Queue;
import io.micronaut.messaging.annotation.MessageBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicInteger;

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

@JMSListener(CONNECTION_FACTORY_BEAN_NAME)  (1)
public class DemoConsumer {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoConsumer.class);

    private final AtomicInteger messageCount = new AtomicInteger(0);

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

    int getMessageCount() {
        return 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/java/example/micronaut/SqsClientFactory.java
package example.micronaut;

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

@Factory  (1)
public class SqsClientFactory {

    @Singleton
    AmazonSQS sqsClient(Environment environment) {  (2)
        return 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/java/example/micronaut/DemoController.java
package example.micronaut;

import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.annotation.Status;

@Controller  (1)
public class DemoController {

    private final DemoProducer demoProducer;

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

    @Post("/demo") (3)
    @Status(HttpStatus.NO_CONTENT)
    public 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. Generate a Micronaut Application Native Executable with GraalVM

We will use GraalVM, the polyglot embeddable virtual machine, to generate a native executable of our Micronaut application.

Compiling native executables ahead of time with GraalVM improves startup time and reduces the memory footprint of JVM-based applications.

Only Java and Kotlin projects support using GraalVM’s native-image tool. Groovy relies heavily on reflection, which is only partially supported by GraalVM.

10.1. Native executable generation

The easiest way to install GraalVM on Linux or Mac is to use SDKMan.io.

Java 11
sdk install java 22.3.r11-grl
If you still use Java 8, use the JDK11 version of GraalVM.
Java 17
sdk install java 22.3.r17-grl

For installation on Windows, or for manual installation on Linux or Mac, see the GraalVM Getting Started documentation.

After installing GraalVM, install the native-image component, which is not installed by default:

gu install native-image

To generate a native executable using Gradle, run:

./gradlew nativeCompile

The native executable is created in build/native/nativeCompile directory and can be run with build/native/nativeCompile/micronautguide.

It is possible to customize the name of the native executable or pass additional parameters to GraalVM:

build.gradle
graalvmNative {
    binaries {
        main {
            imageName.set('mn-graalvm-application') (1)
            buildArgs.add('--verbose') (2)
        }
    }
}
1 The native executable name will now be mn-graalvm-application
2 It is possible to pass extra arguments to build the native executable

Start the native executable and execute the same cURL request as before.

11. Next steps

Explore more features with Micronaut Guides.

Read more about Micronaut JMS.