Skip to content

Commit 4a90b73

Browse files
tomazfernandeswmz7year
andauthored
Add autoconfiguration for queue not found strategy (#1190)
Add SQS QueueNotFoundStrategy auto-configuration support Fixes #1175 --------- Co-authored-by: wmz7year <[email protected]>
1 parent 7570672 commit 4a90b73

File tree

6 files changed

+96
-12
lines changed

6 files changed

+96
-12
lines changed

docs/src/main/asciidoc/_configprops.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
|spring.cloud.aws.sqs.listener.max-concurrent-messages | | The maximum concurrent messages that can be processed simultaneously for each queue. Note that if acknowledgement batching is being used, the actual maximum number of messages inflight might be higher.
7777
|spring.cloud.aws.sqs.listener.max-messages-per-poll | | The maximum number of messages to be retrieved in a single poll to SQS.
7878
|spring.cloud.aws.sqs.listener.poll-timeout | | The maximum amount of time for a poll to SQS.
79+
|spring.cloud.aws.sqs.queue-not-found-strategy | `+++CREATE+++` | Configures strategy when queue not found.
7980
|spring.cloud.aws.sqs.region | | Overrides the default region.
8081

81-
|===
82+
|===

docs/src/main/asciidoc/sqs.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ The Spring Boot Starter for SQS provides the following auto-configuration proper
786786
| <<maxConcurrentMessages, `spring.cloud.aws.sqs.listener.max-inflight-messages-per-queue`>> | Maximum number of inflight messages per queue. | No | 10
787787
| <<maxMessagesPerPoll, `spring.cloud.aws.sqs.listener.max-messages-per-poll`>> | Maximum number of messages to be received per poll. | No | 10
788788
| <<pollTimeout, `spring.cloud.aws.sqs.listener.poll-timeout`>> | Maximum amount of time to wait for messages in a poll. | No | 10 seconds
789+
| `spring.cloud.aws.sqs.queue-not-found-strategy` | The strategy to be used by SqsTemplate and SqsListeners when a queue does not exist. | No | CREATE
789790
|===
790791

791792

spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sqs/SqsAutoConfiguration.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import io.awspring.cloud.sqs.listener.errorhandler.ErrorHandler;
3030
import io.awspring.cloud.sqs.listener.interceptor.AsyncMessageInterceptor;
3131
import io.awspring.cloud.sqs.listener.interceptor.MessageInterceptor;
32+
import io.awspring.cloud.sqs.listener.SqsContainerOptionsBuilder;
3233
import io.awspring.cloud.sqs.operations.SqsTemplate;
3334
import io.awspring.cloud.sqs.operations.SqsTemplateBuilder;
3435
import io.awspring.cloud.sqs.support.converter.SqsMessagingMessageConverter;
@@ -51,6 +52,7 @@
5152
*
5253
* @author Tomaz Fernandes
5354
* @author Maciej Walkowiak
55+
* @author Wei Jiang
5456
* @since 3.0
5557
*/
5658
@AutoConfiguration
@@ -82,6 +84,9 @@ public SqsTemplate sqsTemplate(SqsAsyncClient sqsAsyncClient, ObjectProvider<Obj
8284
SqsTemplateBuilder builder = SqsTemplate.builder().sqsAsyncClient(sqsAsyncClient);
8385
objectMapperProvider
8486
.ifAvailable(om -> builder.configureDefaultConverter(converter -> converter.setObjectMapper(om)));
87+
if (sqsProperties.getQueueNotFoundStrategy() != null) {
88+
builder.configure((options) -> options.queueNotFoundStrategy(sqsProperties.getQueueNotFoundStrategy()));
89+
}
8590
return builder.build();
8691
}
8792

@@ -112,8 +117,9 @@ private void setObjectMapper(SqsMessageListenerContainerFactory<Object> factory,
112117
factory.configure(options -> options.messageConverter(messageConverter));
113118
}
114119

115-
private void configureContainerOptions(ContainerOptionsBuilder<?, ?> options) {
120+
private void configureContainerOptions(SqsContainerOptionsBuilder options) {
116121
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
122+
mapper.from(this.sqsProperties.getQueueNotFoundStrategy()).to(options::queueNotFoundStrategy);
117123
mapper.from(this.sqsProperties.getListener().getMaxConcurrentMessages()).to(options::maxConcurrentMessages);
118124
mapper.from(this.sqsProperties.getListener().getMaxMessagesPerPoll()).to(options::maxMessagesPerPoll);
119125
mapper.from(this.sqsProperties.getListener().getPollTimeout()).to(options::pollTimeout);

spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/sqs/SqsProperties.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package io.awspring.cloud.autoconfigure.sqs;
1717

1818
import io.awspring.cloud.autoconfigure.AwsClientProperties;
19+
import io.awspring.cloud.sqs.listener.QueueNotFoundStrategy;
20+
1921
import java.time.Duration;
2022
import org.springframework.boot.context.properties.ConfigurationProperties;
2123
import org.springframework.lang.Nullable;
@@ -24,6 +26,7 @@
2426
* Properties related to AWS SQS.
2527
*
2628
* @author Tomaz Fernandes
29+
* @author Wei Jiang
2730
* @since 3.0
2831
*/
2932
@ConfigurationProperties(prefix = SqsProperties.PREFIX)
@@ -44,6 +47,26 @@ public void setListener(Listener listener) {
4447
this.listener = listener;
4548
}
4649

50+
@Nullable
51+
private QueueNotFoundStrategy queueNotFoundStrategy;
52+
53+
/**
54+
* Return the strategy to use if the queue is not found.
55+
* @return the {@link QueueNotFoundStrategy}
56+
*/
57+
@Nullable
58+
public QueueNotFoundStrategy getQueueNotFoundStrategy() {
59+
return queueNotFoundStrategy;
60+
}
61+
62+
/**
63+
* Set the strategy to use if the queue is not found.
64+
* @param queueNotFoundStrategy the strategy to set.
65+
*/
66+
public void setQueueNotFoundStrategy(QueueNotFoundStrategy queueNotFoundStrategy) {
67+
this.queueNotFoundStrategy = queueNotFoundStrategy;
68+
}
69+
4770
public static class Listener {
4871

4972
/**

spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sqs/SqsAutoConfigurationIntegrationTest.java

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,26 @@
1616
package io.awspring.cloud.autoconfigure.sqs;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1920

2021
import io.awspring.cloud.autoconfigure.core.AwsAutoConfiguration;
2122
import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
2223
import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
24+
import io.awspring.cloud.sqs.QueueAttributesResolvingException;
2325
import io.awspring.cloud.sqs.annotation.SqsListener;
2426
import io.awspring.cloud.sqs.operations.SqsTemplate;
27+
import org.testcontainers.shaded.org.bouncycastle.util.Arrays;
28+
import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException;
29+
30+
import java.util.concurrent.CompletionException;
2531
import java.util.concurrent.CountDownLatch;
2632
import java.util.concurrent.TimeUnit;
2733
import org.junit.jupiter.api.Test;
2834
import org.springframework.beans.factory.annotation.Autowired;
2935
import org.springframework.boot.autoconfigure.AutoConfigurations;
3036
import org.springframework.boot.test.context.SpringBootTest;
3137
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
38+
import org.springframework.context.ApplicationContextException;
3239
import org.springframework.context.annotation.Bean;
3340
import org.springframework.context.annotation.Configuration;
3441
import org.testcontainers.containers.localstack.LocalStackContainer;
@@ -40,6 +47,7 @@
4047
* Integration tests for {@link SqsAutoConfiguration}.
4148
*
4249
* @author Tomaz Fernandes
50+
* @author Wei Jiang
4351
*/
4452
@SpringBootTest
4553
@Testcontainers
@@ -49,18 +57,30 @@ class SqsAutoConfigurationIntegrationTest {
4957

5058
private static final String PAYLOAD = "Test";
5159

52-
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
53-
.withPropertyValues("spring.cloud.aws.sqs.region=eu-west-1",
54-
"spring.cloud.aws.sqs.endpoint=" + localstack.getEndpoint(),
55-
"spring.cloud.aws.credentials.access-key=noop", "spring.cloud.aws.credentials.secret-key=noop",
56-
"spring.cloud.aws.region.static=eu-west-1")
57-
.withConfiguration(AutoConfigurations.of(RegionProviderAutoConfiguration.class,
58-
CredentialsProviderAutoConfiguration.class, SqsAutoConfiguration.class, AwsAutoConfiguration.class,
59-
ListenerConfiguration.class));
60-
6160
@Container
6261
static LocalStackContainer localstack = new LocalStackContainer(
63-
DockerImageName.parse("localstack/localstack:3.2.0"));
62+
DockerImageName.parse("localstack/localstack:3.2.0"));
63+
64+
static {
65+
localstack.start();
66+
}
67+
68+
private static final String[] BASE_PARAMS = {"spring.cloud.aws.sqs.region=eu-west-1",
69+
"spring.cloud.aws.sqs.endpoint=" + localstack.getEndpoint(),
70+
"spring.cloud.aws.credentials.access-key=noop", "spring.cloud.aws.credentials.secret-key=noop",
71+
"spring.cloud.aws.region.static=eu-west-1"};
72+
73+
private static final AutoConfigurations BASE_CONFIGURATIONS = AutoConfigurations.of(RegionProviderAutoConfiguration.class,
74+
CredentialsProviderAutoConfiguration.class, SqsAutoConfiguration.class, AwsAutoConfiguration.class,
75+
ListenerConfiguration.class);
76+
77+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
78+
.withPropertyValues(BASE_PARAMS)
79+
.withConfiguration(BASE_CONFIGURATIONS);
80+
81+
private final ApplicationContextRunner applicationContextRunnerWithFailStrategy = new ApplicationContextRunner()
82+
.withPropertyValues(Arrays.append(BASE_PARAMS, "spring.cloud.aws.sqs.queue-not-found-strategy=fail"))
83+
.withConfiguration(BASE_CONFIGURATIONS);
6484

6585
@SuppressWarnings("unchecked")
6686
@Test
@@ -73,6 +93,25 @@ void sendsAndReceivesMessage() {
7393
});
7494
}
7595

96+
@Test
97+
void containerReceivesMessageWithFailQueueNotFoundStrategy() {
98+
applicationContextRunnerWithFailStrategy.run(context ->
99+
assertThatThrownBy(() -> context.getBean(SqsTemplate.class).send(to -> to.queue("QUEUE_DOES_NOT_EXISTS").payload(PAYLOAD)))
100+
.isInstanceOf(IllegalStateException.class).cause().isInstanceOf(ApplicationContextException.class).cause()
101+
.isInstanceOf(CompletionException.class).cause().isInstanceOf(QueueAttributesResolvingException.class)
102+
.cause().isInstanceOf(QueueDoesNotExistException.class));
103+
}
104+
105+
@Test
106+
void templateReceivesMessageWithFailQueueNotFoundStrategy() {
107+
applicationContextRunnerWithFailStrategy
108+
.run(context ->
109+
assertThatThrownBy(() -> context.getBean(SqsTemplate.class).receive("QUEUE_DOES_NOT_EXIST", String.class))
110+
.isInstanceOf(IllegalStateException.class).cause().isInstanceOf(ApplicationContextException.class).cause()
111+
.isInstanceOf(CompletionException.class).cause().isInstanceOf(QueueAttributesResolvingException.class)
112+
.cause().isInstanceOf(QueueDoesNotExistException.class));
113+
}
114+
76115
static class Listener {
77116

78117
@Autowired

spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/sqs/SqsAutoConfigurationTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import io.awspring.cloud.sqs.config.SqsMessageListenerContainerFactory;
3333
import io.awspring.cloud.sqs.listener.ContainerOptions;
3434
import io.awspring.cloud.sqs.listener.ContainerOptionsBuilder;
35+
import io.awspring.cloud.sqs.listener.QueueNotFoundStrategy;
3536
import io.awspring.cloud.sqs.listener.errorhandler.AsyncErrorHandler;
3637
import io.awspring.cloud.sqs.listener.interceptor.AsyncMessageInterceptor;
3738
import io.awspring.cloud.sqs.operations.SqsTemplate;
@@ -60,6 +61,7 @@
6061
* Tests for {@link SqsAutoConfiguration}.
6162
*
6263
* @author Tomaz Fernandes
64+
* @author Wei Jiang
6365
*/
6466
class SqsAutoConfigurationTest {
6567

@@ -110,6 +112,18 @@ void withCustomEndpoint() {
110112
});
111113
}
112114

115+
@Test
116+
void withCustomQueueNotFoundStrategy() {
117+
this.contextRunner.withPropertyValues("spring.cloud.aws.sqs.queue-not-found-strategy=fail").run(context -> {
118+
assertThat(context).hasSingleBean(SqsProperties.class);
119+
SqsProperties sqsProperties = context.getBean(SqsProperties.class);
120+
assertThat(context).hasSingleBean(SqsAsyncClient.class);
121+
assertThat(context).hasSingleBean(SqsTemplate.class);
122+
assertThat(context).hasSingleBean(SqsMessageListenerContainerFactory.class);
123+
assertThat(sqsProperties.getQueueNotFoundStrategy()).isEqualTo(QueueNotFoundStrategy.FAIL);
124+
});
125+
}
126+
113127
// @formatter:off
114128
@Test
115129
void customSqsClientConfigurer() {

0 commit comments

Comments
 (0)