DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • End-to-End Event Streaming With Kafka, Spring Boot and AWS SQS/SNS (Production-Ready Code Guide)
  • Caching Mechanisms Using Spring Boot With Redis or AWS ElastiCache
  • Context Search With AWS Bedrock, Cohere Model, and Spring AI
  • Leverage Amazon BedRock Chat Model With Java and Spring AI

Trending

  • Chaos Engineering Has a Blind Spot. Agentic AI Lives in It.
  • Contract-First Integration: Building Scalable Systems With Flyway, OpenAPI, and Kafka
  • How We Diagnosed a Hidden Scheduler Failure in a Docker Swarm Cluster Serving 2 Million Users
  • Beyond Conversation: Mastering Context with Claude Code Skills and Agents
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. AWS SNS (Amazon Simple Notification Service) and Spring Boot With Email as Subscriber

AWS SNS (Amazon Simple Notification Service) and Spring Boot With Email as Subscriber

Learn to use AWS SNS (Amazon Simple Notification Service) messaging service to publish a message to an email address as a subscriber using Spring Boot.

By 
Milan Karajovic user avatar
Milan Karajovic
·
Aug. 05, 25 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
3.0K Views

Join the DZone community and get the full member experience.

Join For Free

The concepts of "topic" and "subscribe" are often linked, especially in contexts like messaging systems, event-driven architectures, or content platforms.

  • Publisher: This is the source or entity that produces messages or events. The publisher doesn't need to know who will consume its messages.
  • Topic: This acts as a channel or intermediary that categorizes messages. Publishers post messages to specific topics, and subscribers listen to those topics. It's used in systems like message brokers (e.g., RabbitMQ, AWS SNS, Apache Kafka) to allow publishers to send messages without worrying about who will receive them.
  • Subscriber: These are the entities that consume the messages from the topics they're interested in. Subscribers can dynamically choose topics to receive only the information they need.

Amazon SNS (Simple Notification Service) Topic

Amazon SNS provides message delivery from publishers to subscribers using the pub/sub pattern. Publishers send messages to an SNS topic, and subscribers receive those messages through their chosen endpoints.

Topic Types: SNS supports two types of topics:

  • Standard Topics: Allow high throughput and best-effort message ordering, at least one message delivery
  • FIFO Topics: Ensure message order and exactly-once message delivery.

Subscribers can be other applications, an Amazon SQS queue, an AWS Lambda function, or an HTTPS endpoint. We can see that in the picture below:

Amazon SNS

Create a Topic and Subscribe to the Topic

Create a User in IAM

On the AWS dashboard, select the IAM service (manages access to AWS resources). Create a user or use an existing user.

  • For the created user, add the permission policy AmazonSNSFullAccess.
  • On the card security credentials, you can see the option 'Access Keys' (access key - go to the process where you will create user_accessKeys.csv). In this file, you can see the Access key ID and Secret access key. These values you will use in the Spring Boot application, in the file: AppListener.java(/spring-boot-aws-sns/src/main/java/rs/karajovic/milan/config/AppListener.java).

Create a Topic in Amazon SNS

On the AWS dashboard, choose Amazon SNS (Simple Notification Service) -> Go to link Topics -> Create topic:

  • Standard
  • Give a name for the topic
  • Create topic

Subscribe to the Topic 

Go to link Topics -> Choose created topic -> Create subscription:

  • There, you can see Topic ARN.
  • Also, you can see different protocols: Amazon Kinesis Data Firehose, Amazon SQS, AWS Lambda, Email, Email-JSON, HTTP, HTTPS, Platform application endpoint, SMS. For our demo, we will choose: Email-JSON.

When you choose Topics and click on your topic, you can see fields: Name and ARN. Content of these fields you will use in the project, in the file: application.properties (/spring-boot-aws-sns/src/main/resources/application.properties).

Tech Stack

  • Java 17
  • SNS Amazon-AWSSDK
  • JPA
  • Maven
  • Lombok
  • JUnit
  • IAM
  • SNS (Simple Notification Service)

Create a Spring Boot application using Spring Initializr using specific dependencies, which are shown in the pom.xml:

XML
 
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
  <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>sns</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>


Allow the Application to Connect to AWS

 Create an application listener in which the AWS access key and secret key must be set up in the Java system properties, allowing the app to connect to AWS. We got these properties during the step of creating a context in IAM, which was explained in the previous step.

Java
 
package rs.karajovic.milan.config;

import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

public class AppListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
    	
    	// accessKeyId & for AWS user
        System.setProperty("aws.accessKeyId", "fill with accessKeyId & for AWS user");
        // secretAccessKey for AWS user
        System.setProperty("aws.secretAccessKey", "fill with secretAccessKey for AWS user");
    	
    }
}


Application listener should be added to the application. 

Java
 
@SpringBootApplication
public class SpringBootAwsSns {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(SpringBootAwsSns.class);
        app.addListeners(new AppListener());
        app.run(args);
    }
}


Set Up SNS Properties 

In application.properties, set values for aws-sns region and topicArn. We got these properties in the step of creating a topic in Amazon SNS, which was explained in the step before. 

Textile
 
#### AWS ### -
aws.sns.region=fill with region where you crated sns
aws.sns.topicArn=fill with created arn for test-top-arn

management.endpoints.web.exposure.include=*


Configuration SNS Client

Now we need to configure the SNS Client that will publish messages to the topic. We created a class that reads the AWS SNS properties from the properties file. It is in the class /spring-boot-aws-sns/src/main/java/rs/karajovic/milan/config/AwsProperties.java.

Java
 
package rs.karajovic.milan.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.NotNull;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

@Configuration
@ConfigurationProperties(prefix = "aws.sns")
public class AwsProperties {

	@Getter @Setter
    @NotNull
    private String region;

	@Getter @Setter
    @NotNull
    private String topicArn;


}


Then, configure an SNS Client with the appropriate region. 

Java
 
package rs.karajovic.milan.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

@Configuration
public class SnsConfig {

    @Autowired
    private AwsProperties awsProperties;

    @Bean
    public SnsClient snsClient() {
        return SnsClient.builder()
                .region(Region.of(awsProperties.getRegion()))
                .build();
    }
}


Build a Message With Message Attributes

In our scenario, subscribers to the topic wish to receive notifications about changes in temperature, whether it increases or decreases. Create the Message class to specify the attributes that are relevant to these subscribers.

Java
 
package rs.karajovic.milan.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

@Configuration
public class SnsConfig {

    @Autowired
    private AwsProperties awsProperties;

    @Bean
    public SnsClient snsClient() {
        return SnsClient.builder()
                .region(Region.of(awsProperties.getRegion()))
                .build();
    }
}
Java
 
package rs.karajovic.milan.model;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

public enum EventType {
    DROP, INCREASE
}


Next, we create a builder that constructs an Amazon PublishRequest object from a given message. This message leverages Amazon SNS message attributes, which allow for greater flexibility. One key advantage of using these attributes is that they enable subscribers to filter their subscriptions based on specific attributes, ensuring they only receive the notifications that are relevant to their interests. 

Java
 
ackage rs.karajovic.milan.model;

import software.amazon.awssdk.services.sns.model.MessageAttributeValue;
import software.amazon.awssdk.services.sns.model.PublishRequest;

import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

public class RequestBuilder {
    public static final String COUNTRY = "Country";
    public static final String REGION = "Region";
    public static final String EVENT_TYPE = "EventType";
    public static final String CITY = "City";
    public static final String NEW_TEMPERATURE = "NewTemperature";
    public static final String DEFAULT_MESSAGE_BODY = "Please see attributes.";


    public static PublishRequest build(String topicArn, Message message) {
        Map<String, MessageAttributeValue> attributes = new HashMap<>();
        attributes.put(COUNTRY, buildAttribute(message.getCountry(), "String"));
        attributes.put(REGION, buildAttribute(message.getRegion(), "String"));
        attributes.put(EVENT_TYPE, buildAttribute(message.getEventType().toString(), "String"));
        attributes.put(CITY, buildAttribute(message.getCity(), "String"));
        attributes.put(NEW_TEMPERATURE, buildAttribute(message.getNewTemperature().toString(), "Number"));

        PublishRequest request = PublishRequest.builder()
                .topicArn(topicArn)
                .message(DEFAULT_MESSAGE_BODY)
                .messageAttributes(attributes)
                .build();

        return request;
    }

    private static MessageAttributeValue buildAttribute(String value, String dataType) {
        return MessageAttributeValue.builder()
                .dataType(dataType)
                .stringValue(value)
                .build();
    }
}


Publish a Message

Create an endpoint that publishes a message to the specified topic. Upon execution, the endpoint provides the status code, the response message, and the unique message ID generated by Amazon SNS for the published message. In the event of an exception, the endpoint will return the corresponding status code along with the error message as received from Amazon SNS. The next step involves transforming the message into an Amazon PublishRequest and proceeding to publish it.

Java
 
package rs.karajovic.milan.controller;

import rs.karajovic.milan.model.Message;
import rs.karajovic.milan.model.SnsResponse;
import rs.karajovic.milan.service.MessagePublisher;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

@RestController
public class MessageController {

    private final MessagePublisher messagePublisher;

    public MessageController(MessagePublisher messagePublisher) {
        this.messagePublisher = messagePublisher;
    }

    @PostMapping(value = "/publish")
    @ResponseStatus(HttpStatus.CREATED)
    public SnsResponse publishMessage(@RequestBody Message message) {
        return messagePublisher.publish(message);
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(RuntimeException.class)
    private String handleException(RuntimeException e) {
        return e.getMessage();
    }
}


Also, we will create a model SNS response, which will be used to receive the response from the publish endpoint: 

Java
 
package rs.karajovic.milan.model;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * 
 * @author Milan Karajovic <[email protected]>
 *
 */

@NoArgsConstructor
@AllArgsConstructor
@Getter
@EqualsAndHashCode
public class SnsResponse {
    private Integer statusCode;
    private String message;
    private String publishedMessageId;
    
    @Override
    public String toString() {
        return "SnsResponse{statusCode=" + statusCode + 
               ", message='" + message + '\'' + 
               ", publishedMessageId='" + publishedMessageId + "'}";
    }

}


The code handles various exceptions that Amazon SNS might throw, with SdkException handling as a general catch-all for other potential exceptions.

Send a Message in SNS Using Local Environment Development Tool

Run the created Spring Boot app and send a message to the publish endpoint using Postman as shown in the picture:

Send a message to the publish endpoint using Postman

The response body indicates that Amazon SNS returned a 200 status code along with the message “OK” and a message ID, verifying that the message was successfully published.
Message was successfully published

Check the email address that was subscribed to the topic earlier; you should find a notification from AWS there. 

AWS notification message

Docker and docker-compose

This Spring Boot application can also be dockerized. We are using the Dockerfile and docker-compose.yml files, which are in the root of the project.

Dockerfile:

Dockerfile

When an image is created, we start the container using the docker-compose.yml file:

docker-compose.yml file

When the container is started, we can send a message to the publish endpoint using Postman as shown in the picture:
Send a message to the publish endpoint

Source Code

You can find the source code here.

AWS Notification service Spring Boot

Opinions expressed by DZone contributors are their own.

Related

  • End-to-End Event Streaming With Kafka, Spring Boot and AWS SQS/SNS (Production-Ready Code Guide)
  • Caching Mechanisms Using Spring Boot With Redis or AWS ElastiCache
  • Context Search With AWS Bedrock, Cohere Model, and Spring AI
  • Leverage Amazon BedRock Chat Model With Java and Spring AI

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook