I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll explore how to use Amazon’s SQS (Simple Queue Service) using the Java SDK.

2. Prerequisites

The Maven dependencies, AWS account settings, and client connection needed to use the Amazon AWS SDK for SQS are the same as in this article here.

Assuming we’ve created an instance of AWSCredentials, as described in the previous article, we can go ahead and create our SQS client:

AmazonSQS sqs = AmazonSQSClientBuilder.standard()
  .withCredentials(new AWSStaticCredentialsProvider(credentials))
  .withRegion(Regions.US_EAST_1)
  .build();

3. Creating Queues

Once we’ve set up our SQS client, creating queues is fairly straightforward.

3.1. Creating a Standard Queue

Let’s see how we can create a Standard Queue. To do this, we’ll need to create an instance of CreateQueueRequest:

CreateQueueRequest createStandardQueueRequest = new CreateQueueRequest("baeldung-queue");
String standardQueueUrl = sqs.createQueue(createStandardQueueRequest).getQueueUrl();

3.2. Creating a FIFO Queue

Creating a FIFO is similar to creating a Standard Queue. We’ll still use an instance of CreateQueueRequest, as we did previously. Only this time, we’ll have to pass in queue attributes, and set the FifoQueue attribute to true:

Map<String, String> queueAttributes = new HashMap<>();
queueAttributes.put("FifoQueue", "true");
queueAttributes.put("ContentBasedDeduplication", "true");
CreateQueueRequest createFifoQueueRequest = new CreateQueueRequest(
  "baeldung-queue.fifo").withAttributes(queueAttributes);
String fifoQueueUrl = sqs.createQueue(createFifoQueueRequest)
  .getQueueUrl();

4. Posting Messages to Queues

Once we’ve got our queues set up, we can start sending messages.

4.1. Posting a Message to a Standard Queue

To send messages to a standard queue, we’ll have to create an instance of SendMessageRequest.

Then we attach a map of message attributes to this request:

Map<String, MessageAttributeValue> messageAttributes = new HashMap<>();
messageAttributes.put("AttributeOne", new MessageAttributeValue()
  .withStringValue("This is an attribute")
  .withDataType("String"));  
    
SendMessageRequest sendMessageStandardQueue = new SendMessageRequest()
  .withQueueUrl(standardQueueUrl)
  .withMessageBody("A simple message.")
  .withDelaySeconds(30)
  .withMessageAttributes(messageAttributes);

sqs.sendMessage(sendMessageStandardQueue);

The withDelaySeconds() specifies after how long the message should arrive on the queue.

4.2. Posting a Message to a FIFO Queue

The only difference, in this case, is that we’ll have to specify the group to which the message belongs:

SendMessageRequest sendMessageFifoQueue = new SendMessageRequest()
  .withQueueUrl(fifoQueueUrl)
  .withMessageBody("Another simple message.")
  .withMessageGroupId("baeldung-group-1")
  .withMessageAttributes(messageAttributes);

As you can see in the code example above, we specify the group by using withMessageGroupId().

4.3. Posting Multiple Messages to a Queue

We can also post multiple messages to a queue, using a single request. We’ll create a list of SendMessageBatchRequestEntry which we’ll send using an instance of SendMessageBatchRequest:

List <SendMessageBatchRequestEntry> messageEntries = new ArrayList<>();
messageEntries.add(new SendMessageBatchRequestEntry()
  .withId("id-1")
  .withMessageBody("batch-1")
  .withMessageGroupId("baeldung-group-1"));
messageEntries.add(new SendMessageBatchRequestEntry()
  .withId("id-2")
  .withMessageBody("batch-2")
  .withMessageGroupId("baeldung-group-1"));

SendMessageBatchRequest sendMessageBatchRequest
 = new SendMessageBatchRequest(fifoQueueUrl, messageEntries);
sqs.sendMessageBatch(sendMessageBatchRequest);

5. Reading Messages from Queues

We can receive messages from our queues by invoking the receiveMessage() method on an instance of ReceiveMessageRequest:

ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest(fifoQueueUrl)
  .withWaitTimeSeconds(10)
  .withMaxNumberOfMessages(10);

List<Message> sqsMessages = sqs.receiveMessage(receiveMessageRequest).getMessages();

Using withMaxNumberOfMessages(), we specify how many messages to get from the queue — although it should be noted that the maximum is 10.

The method withWaitTimeSeconds() enables long-polling. Long polling is a way to limit the number of receive message requests we send to SQS. 

Simply put, this means that we’ll wait up to the specified number of seconds to retrieve a message. If there are no messages in the queue for that duration, then the request will return empty. If a message arrives on the queue during that time, it will be returned.

We can get the attributes and body of a given message:

sqsMessages.get(0).getAttributes();
sqsMessages.get(0).getBody();

6. Deleting a Message from a Queue

To delete a message, we’ll use a DeleteMessageRequest:

sqs.deleteMessage(new DeleteMessageRequest()
  .withQueueUrl(fifoQueueUrl)
  .withReceiptHandle(sqsMessages.get(0).getReceiptHandle()));

7. Dead Letter Queues

A dead letter queue must be of the same type as its base queue — it must be FIFO if the base queue is FIFO, and standard if the base queue is standard. For this example, we’ll use a standard queue.

The first thing we need to do is to create what will become our dead letter queue:

String deadLetterQueueUrl = sqs.createQueue("baeldung-dead-letter-queue").getQueueUrl();

Next, we’ll get our newly created queue’s ARN (Amazon Resource Name):

GetQueueAttributesResult deadLetterQueueAttributes = sqs.getQueueAttributes(
  new GetQueueAttributesRequest(deadLetterQueueUrl)
    .withAttributeNames("QueueArn"));

String deadLetterQueueARN = deadLetterQueueAttributes.getAttributes()
  .get("QueueArn");

Finally, we set this newly created queue to be our original standard queue’s dead letter queue:

SetQueueAttributesRequest queueAttributesRequest = new SetQueueAttributesRequest()
  .withQueueUrl(standardQueueUrl)
  .addAttributesEntry("RedrivePolicy",
    "{\"maxReceiveCount\":\"2\", "
      + "\"deadLetterTargetArn\":\"" + deadLetterQueueARN + "\"}");

sqs.setQueueAttributes(queueAttributesRequest);

The JSON packet we set in the addAttributesEntry() method when building our SetQueueAttributesRequest instance contains the information we need: the maxReceiveCount is 2, which means that if a message is received this many times, it’s assumed to haven’t been processed correctly, and is sent to our dead letter queue.

The deadLetterTargetArn attribute points our standard queue to our newly created dead letter queue.

8. Monitoring

We can check how many messages are currently in a given queue, and how many are in flight with the SDK. First, we’ll need to create a GetQueueAttributesRequest. 

From there we’ll check the state of the queue:

GetQueueAttributesRequest getQueueAttributesRequest 
  = new GetQueueAttributesRequest(standardQueueUrl)
    .withAttributeNames("All");
GetQueueAttributesResult getQueueAttributesResult 
  = sqs.getQueueAttributes(getQueueAttributesRequest);
System.out.println(String.format("The number of messages on the queue: %s", 
  getQueueAttributesResult.getAttributes()
    .get("ApproximateNumberOfMessages")));
System.out.println(String.format("The number of messages in flight: %s", 
  getQueueAttributesResult.getAttributes()
    .get("ApproximateNumberOfMessagesNotVisible")));

More in-depth monitoring can be achieved using Amazon Cloud Watch.

9. Conclusion

In this article, we’ve seen how to manage SQS queues using the AWS Java SDK.

As usual, all code samples used in the article can be found over on GitHub.

I just announced the new Spring Boot 2 material, coming modules in REST With Spring:

>> CHECK OUT THE LESSONS