Skip to content

Commit d90287f

Browse files
SNS integration based on AWS SDK v2 (#276)
Co-authored-by: Maciej Walkowiak <walkowiak.maciej@yahoo.com>
1 parent 87aaa72 commit d90287f

File tree

98 files changed

+2767
-3394
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+2767
-3394
lines changed

.idea/misc.xml

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/src/main/asciidoc/sns.adoc

Lines changed: 116 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,149 @@
1-
=== SNS support
2-
Amazon SNS is a publish-subscribe messaging system that allows clients to publish notification to a particular topic. Other
3-
interested clients may subscribe using different protocols like HTTP/HTTPS, e-mail or an Amazon SQS queue to receive the messages.
1+
[#spring-cloud-aws-sns]
2+
== Spring Cloud AWS SNS
43

5-
The next graphic shows a typical example of an Amazon SNS architecture.
4+
https://aws.amazon.com/sns/[SNS] is a pub/sub messaging service that allows clients to publish notifications to a particuluar topic.
5+
A Spring Boot starter is provided to auto-configure SNS integration beans.
66

7-
image::sns-overview.png[SNS Overview]
7+
Maven coordinates, using <<index.adoc#bill-of-materials, Spring Cloud AWS BOM>>:
88

9-
Spring Cloud AWS supports Amazon SNS by providing support to send notifications with a `NotificationMessagingTemplate` and
10-
to receive notifications with the HTTP/HTTPS endpoint using the Spring Web MVC `@Controller` based programming model. Amazon
11-
SQS based subscriptions can be used with the annotation-driven message support that is provided by the Spring Cloud AWS messaging module.
9+
[source,xml]
10+
----
11+
<dependency>
12+
<groupId>io.awspring.cloud</groupId>
13+
<artifactId>spring-cloud-aws-starter-sns</artifactId>
14+
</dependency>
15+
----
16+
17+
=== Sending Notifications
18+
19+
==== SNS Template
20+
21+
The starter automatically configures and registers a `SnsTemplate` bean providing higher level abstractions for sending SNS notifications.
22+
`SnsTemplate` implements Spring Messaging abstractions making it easy to combine with other messaging technologies compatible with Spring Messaging.
23+
24+
It supports sending notifications with payload of type:
1225

13-
==== Sending a message
14-
The `NotificationMessagingTemplate` contains two convenience methods to send a notification. The first one specifies the
15-
destination using a `String` which is going to be resolved against the SNS API. The second one takes no destination
16-
argument and uses the default destination. All the usual send methods that are available on the `MessageSendingOperations`
17-
are implemented but are less convenient to send notifications because the subject must be passed as header.
26+
* `String` - using `org.springframework.messaging.converter.StringMessageConverter`
27+
* `Object` - which gets serialized to JSON using `org.springframework.messaging.converter.MappingJackson2MessageConverter` and Jackson's `com.fasterxml.jackson.databind.ObjectMapper` autoconfigured by Spring Boot.
1828

19-
[NOTE]
20-
====
21-
Currently only `String` payloads can be sent using the `NotificationMessagingTemplate` as this is the expected
22-
type by the SNS API.
23-
====
29+
Additionally, it exposes handful of methods supporting `org.springframework.messaging.Message`.
2430

25-
[source,java,indent=0]
31+
[source,java]
2632
----
27-
import com.amazonaws.services.sns.AmazonSNS;
28-
import org.springframework.beans.factory.annotation.Autowired;
29-
import io.awspring.cloud.messaging.core.NotificationMessagingTemplate;
33+
import io.awspring.cloud.sns.core.SnsTemplate;
3034
31-
public class SnsNotificationSender {
35+
import org.springframework.messaging.Message;
36+
import org.springframework.messaging.support.MessageBuilder;
3237
33-
private final NotificationMessagingTemplate notificationMessagingTemplate;
38+
public class NotificationService {
39+
private final SnsTemplate snsTemplate;
3440
35-
@Autowired
36-
public SnsNotificationSender(AmazonSNS amazonSns) {
37-
this.notificationMessagingTemplate = new NotificationMessagingTemplate(amazonSns);
41+
NotificationService(SnsTemplate snsTemplate) {
42+
this.snsTemplate = snsTemplate;
3843
}
3944
40-
public void send(String subject, String message) {
41-
this.notificationMessagingTemplate.sendNotification("physicalTopicName", message, subject);
45+
void sendNotification() {
46+
// sends String payload
47+
snsTemplate.sendNotification("topic-arn", "payload", "subject");
48+
// sends object serialized to JSON
49+
snsTemplate.sendNotification("topic-arn", new Person("John", "Doe"), "subject");
50+
// sends a Spring Messaging Message
51+
Message<String> message = MessageBuilder.withPayload("payload")
52+
.setHeader("header-name", "header-value")
53+
.build();
54+
snsTemplate.send("topic-arn", message);
4255
}
4356
}
4457
----
4558

46-
This example constructs a new `NotificationMessagingTemplate` by passing an `AmazonSNS` client as argument. In the `send`
47-
method the convenience `sendNotification` method is used to send a `message` with `subject` to an SNS topic. The
48-
destination in the `sendNotification` method is a string value that must match the topic name defined on AWS. This value
49-
is resolved at runtime by the Amazon SNS client. Optionally a `ResourceIdResolver` implementation can be passed to the
50-
`NotificationMessagingTemplate` constructor to resolve resources by logical name when running inside a CloudFormation stack.
51-
(See <<Managing cloud environments>> for more information about resource name resolution.)
59+
If autoconfigured converters do not meet your needs, you can provide a custom `SnsTemplate` bean with a message converter of your choice.
5260

53-
It is recommended to use the XML messaging namespace to create `NotificationMessagingTemplate` as it will automatically
54-
configure the SNS client to setup the default converter.
61+
When sending SNS notification, it is required to provide a topic ARN. Spring Cloud AWS simplifies it and allows providing a topic name instead, under a condition that topic with that name has already been created.
62+
Otherwise, Spring Cloud AWS will make an attempt to create topic with this name with a first call.
5563

56-
[source,xml,indent=0]
57-
----
58-
<aws-messaging:notification-messaging-template id="notificationMessagingTemplate" />
59-
----
64+
The behavior of resolving topic ARN by a topic name can be altered by providing a custom bean of type `io.awspring.cloud.sns.core.TopicArnResolver`.
65+
66+
==== SNS Operations
6067

61-
FIFO SNS Topics have additional required and optional request parameters. For example MessageGroupId is a required
62-
parameter for a FIFO topic. These parameters can be set by specifying them in the headers map.
63-
Spring Cloud AWS extracts the keys and sets the parameters on the underlying SNS SendMessage request.
68+
Because of Spring Messaging compatibility, `SnsTemplate` exposes many methods that you may not need if you don't need Spring Messaging abstractions.
69+
In such case, we recommend using `SnsOperations` - an interface implemented by `SnsTemplate`, that exposes a convenient method for sending SNS notification, including support for FIFO topics.
6470

65-
To specify message attributes on the SNS SendMessage request, additional headers can be added to the header map.
71+
[source,java]
72+
----
73+
import io.awspring.cloud.sns.core.SnsNotification;
74+
import io.awspring.cloud.sns.core.SnsOperations;
75+
import io.awspring.cloud.sns.core.SnsTemplate;
76+
77+
public class NotificationService {
78+
private final SnsOperations snsOperations;
6679
67-
This example shows how to add the MessageGroupId parameter (required for FIFO topics) and MessageDeduplicationId parameter
68-
(optional) to the request. The additional header is added as a MessageAttribute. The attribute Type is based on the java
69-
type of the value by Spring Cloud AWS.
80+
NotificationService(SnsOperations snsOperations) {
81+
this.snsOperations = snsOperations;
82+
}
7083
71-
[source,java,indent=0]
84+
void sendNotification() {
85+
SnsNotification<Person> notification = SnsNotification.builder(new Person("John", "Doe"))
86+
.deduplicationId("..")
87+
.groupId("..")
88+
.build();
89+
snsOperations.sendNotification("topic-arn", notification);
90+
}
91+
}
7292
----
73-
import com.amazonaws.services.sns.AmazonSNS;
74-
import io.awspring.cloud.messaging.core.NotificationMessagingTemplate;
75-
import io.awspring.cloud.messaging.core.TopicMessageChannel;
76-
import org.springframework.beans.factory.annotation.Autowired;
7793

78-
import java.util.HashMap;
94+
=== Using SNS Client
95+
96+
To have access to all lower level SNS operations, we recommend using `SnsClient` from AWS SDK. `SnsClient` bean is autoconfigured by `SnsAutoConfiguration`.
7997

80-
public class SnsNotificationSender {
98+
If autoconfigured `SnsClient` bean configuration does not meet your needs, it can be replaced by creating a custom bean of type `SnsClient`.
8199

82-
private final NotificationMessagingTemplate notificationMessagingTemplate;
100+
[source,java]
101+
----
102+
import software.amazon.awssdk.services.sns.SnsClient;
103+
104+
public class NotificationService {
105+
private final SnsClient snsClient;
83106
84-
@Autowired
85-
public SnsNotificationSender(AmazonSNS amazonSns) {
86-
this.notificationMessagingTemplate = new NotificationMessagingTemplate(amazonSns);
107+
public NotificationService(SnsClient snsClient) {
108+
this.snsClient = snsClient;
87109
}
88110
89-
public void send(String message, String messageGroupId, String messageDeduplicationId) {
90-
HashMap<String, Object> headers = new HashMap<>();
91-
headers.put(TopicMessageChannel.MESSAGE_GROUP_ID_HEADER, messageGroupId);
92-
headers.put(TopicMessageChannel.MESSAGE_DEDUPLICATION_ID_HEADER, messageDeduplicationId);
93-
headers.put("attributeName", "attributeValue");
94-
this.notificationMessagingTemplate.convertAndSend("physicalTopicName", message, headers);
111+
void sendNotification() {
112+
snsClient.publish(request -> request.topicArn("sns-topic-arn").message("payload"));
95113
}
96114
}
97115
----
98116

99-
==== Annotation-driven HTTP notification endpoint
117+
=== Annotation-driven HTTP notification endpoint
118+
100119
SNS supports multiple endpoint types (SQS, Email, HTTP, HTTPS), Spring Cloud AWS provides support for HTTP(S) endpoints.
101120
SNS sends three type of requests to an HTTP topic listener endpoint, for each of them annotations are provided:
102121

103122
* Subscription request -> `@NotificationSubscriptionMapping`
104123
* Notification request -> `@NotificationMessageMapping`
105124
* Unsubscription request -> `@NotificationUnsubscribeMapping`
106125

107-
[NOTE]
108-
====
109-
Since 2.4.0 verification has been introduced for Notification request and is turned on by default. Verification is using same region as SNSClient is.
110-
To turn off verification simply set property 'cloud.aws.sns.verification=false'.
111-
With Localstack verification won't work, so you have to set property to 'false' if you want `@NotificationMessageMapping` to work properly.
112-
For more information about SNS verification https://docs.awspring.io/spring-cloud-aws/docs/2.3.3/reference/html/index.html [here].
113-
====
126+
HTTP endpoints are based on Spring MVC controllers. Spring Cloud AWS added some custom argument resolvers to extract the message and subject out of the notification requests.
114127

115-
HTTP endpoints are based on Spring MVC controllers. Spring Cloud AWS added some custom argument resolvers to extract
116-
the message and subject out of the notification requests.
128+
Example of integration:
117129

118-
[source,java,indent=0]
130+
[source,java]
119131
----
132+
import io.awspring.cloud.sns.annotation.endpoint.NotificationMessageMapping;
133+
import io.awspring.cloud.sns.annotation.endpoint.NotificationSubscriptionMapping;
134+
import io.awspring.cloud.sns.annotation.endpoint.NotificationUnsubscribeConfirmationMapping;
135+
import io.awspring.cloud.sns.annotation.handlers.NotificationMessage;
136+
import io.awspring.cloud.sns.annotation.handlers.NotificationSubject;
137+
import io.awspring.cloud.sns.handlers.NotificationStatus;
138+
import org.springframework.stereotype.Controller;
139+
import org.springframework.web.bind.annotation.RequestMapping;
140+
120141
@Controller
121142
@RequestMapping("/topicName")
122143
public class NotificationTestController {
123144
124145
@NotificationSubscriptionMapping
125-
public void handleSubscriptionMessage(NotificationStatus status) throws IOException {
146+
public void handleSubscriptionMessage(NotificationStatus status) {
126147
//We subscribe to start receive the message
127148
status.confirmSubscription();
128149
}
@@ -140,30 +161,17 @@ public class NotificationTestController {
140161
}
141162
----
142163

143-
[CAUTION]
144-
====
145-
Currently it is not possible to define the mapping URL on the method level therefore the `RequestMapping` must
146-
be done at type level and must contain the full path of the endpoint.
147-
====
148-
149-
This example creates a new Spring MVC controller with three methods to handle the three requests listed above. In order
150-
to resolve the arguments of the `handleNotificationMessage` methods a custom argument resolver must be registered. The
151-
XML configuration is listed below.
164+
=== Configuration
152165

153-
[source,xml,indent=0]
154-
----
155-
<mvc:annotation-driven>
156-
<mvc:argument-resolvers>
157-
<ref bean="notificationResolver" />
158-
</mvc:argument-resolvers>
159-
</mvc:annotation-driven>
160-
161-
<aws-messaging:notification-argument-resolver id="notificationResolver" />
162-
----
166+
The Spring Boot Starter for SNS provides the following configuration options:
163167

164-
The `aws-messaging:notification-argument-resolver` element registers three argument resolvers:
165-
`NotificationStatusHandlerMethodArgumentResolver`, `NotificationMessageHandlerMethodArgumentResolver`,
166-
and `NotificationSubjectHandlerMethodArgumentResolver`.
168+
[cols="2,3,1,1"]
169+
|===
170+
| Name | Description | Required | Default value
171+
| `spring.cloud.aws.sns.enabled` | Enables the SNS integration. | No | `true`
172+
| `spring.cloud.aws.sns.endpoint` | Configures endpoint used by `SnsClient`. | No | `http://localhost:4566`
173+
| `spring.cloud.aws.sns.region` | Configures region used by `SnsClient`. | No | `eu-west-1`
174+
|===
167175

168176
==== IAM Permissions
169177
Following IAM permissions are required by Spring Cloud AWS:
@@ -179,6 +187,10 @@ Following IAM permissions are required by Spring Cloud AWS:
179187
| To use Annotation-driven HTTP notification endpoint
180188
| `sns:ConfirmSubscription`
181189

190+
| For resolving topic name to ARN
191+
| `sns:CreateTopic`
192+
193+
182194

183195
|===
184196

@@ -201,48 +213,12 @@ Sample IAM policy granting access to SNS:
201213
"Effect": "Allow",
202214
"Action": "sns:ListTopics",
203215
"Resource": "*"
216+
},
217+
{
218+
"Effect": "Allow",
219+
"Action": "sns:CreateTopic",
220+
"Resource": "*"
204221
}
205222
]
206223
}
207224
----
208-
209-
=== Using CloudFormation
210-
Amazon SQS queues and SNS topics can be configured within a stack and then be used by applications. Spring Cloud AWS
211-
also supports the lookup of stack-configured queues and topics by their logical name with the resolution to the physical
212-
name. The example below shows an SNS topic and SQS queue configuration inside a CloudFormation template.
213-
214-
[source,json,indent=0]
215-
----
216-
"LogicalQueueName": {
217-
"Type": "AWS::SQS::Queue",
218-
"Properties": {
219-
}
220-
},
221-
"LogicalTopicName": {
222-
"Type": "AWS::SNS::Topic",
223-
"Properties": {
224-
}
225-
}
226-
----
227-
228-
The logical names `LogicalQueueName` and `LogicalTopicName` can then be used in the configuration and in the application
229-
as shown below:
230-
231-
[source,xml,indent=0]
232-
----
233-
<aws-messaging:queue-messaging-template default-destination="LogicalQueueName" />
234-
235-
<aws-messaging:notification-messaging-template default-destination="LogicalTopicName" />
236-
----
237-
238-
[source,java,indent=0]
239-
----
240-
@SqsListener("LogicalQueueName")
241-
public void receiveQueueMessages(Person person) {
242-
// Logical names can also be used with messaging templates
243-
this.notificationMessagingTemplate.sendNotification("anotherLogicalTopicName", "Message", "Subject");
244-
}
245-
----
246-
247-
When using the logical names like in the example above, the stack can be created on different environments without any
248-
configuration or code changes inside the application.

docs/src/main/asciidoc/spring-cloud-aws.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ include::core.adoc[]
125125

126126
include::s3.adoc[]
127127

128+
include::sns.adoc[]
129+
128130

129131
//
130132
//===== AWS EKS IAM Roles for Service Accounts configuration

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
<module>spring-cloud-aws-parameter-store</module>
4444
<module>spring-cloud-aws-secrets-manager</module>
4545
<module>spring-cloud-aws-ses</module>
46+
<module>spring-cloud-aws-sns</module>
4647
<module>spring-cloud-aws-s3-parent</module>
4748
<module>spring-cloud-aws-starters</module>
4849
<module>spring-cloud-aws-samples</module>

0 commit comments

Comments
 (0)