/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.aws.messaging.listener;
import com.amazonaws.AmazonClientException;
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.amazonaws.services.sqs.buffered.AmazonSQSBufferedAsyncClient;
import com.amazonaws.services.sqs.model.DeleteMessageRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesRequest;
import com.amazonaws.services.sqs.model.GetQueueAttributesResult;
import com.amazonaws.services.sqs.model.GetQueueUrlRequest;
import com.amazonaws.services.sqs.model.GetQueueUrlResult;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.MessageAttributeValue;
import com.amazonaws.services.sqs.model.OverLimitException;
import com.amazonaws.services.sqs.model.QueueAttributeName;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.cloud.aws.core.support.documentation.RuntimeUse;
import org.springframework.cloud.aws.messaging.config.annotation.EnableSqs;
import org.springframework.cloud.aws.messaging.listener.annotation.SqsListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.MimeType;
import org.springframework.util.StopWatch;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import static org.mockito.MockitoAnnotations.initMocks;
/**
* @author Agim Emruli
* @author Alain Sahli
* @since 1.0
*/
public class SimpleMessageListenerContainerTest {
@Rule
public final ExpectedException expectedException = ExpectedException.none();
@Captor
private ArgumentCaptor<org.springframework.messaging.Message<String>> stringMessageCaptor;
@Before
public void setUp() throws Exception {
initMocks(this);
}
@Test
public void testWithDefaultTaskExecutorAndNoBeanName() throws Exception {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setAmazonSqs(mock(AmazonSQSAsync.class, withSettings().stubOnly()));
container.setMessageHandler(mock(QueueMessageHandler.class));
container.afterPropertiesSet();
ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor) container.getTaskExecutor();
assertNotNull(taskExecutor);
assertEquals(SimpleMessageListenerContainer.class.getSimpleName() + "-", taskExecutor.getThreadNamePrefix());
}
@Test
public void testWithDefaultTaskExecutorAndBeanName() throws Exception {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setAmazonSqs(mock(AmazonSQSAsync.class, withSettings().stubOnly()));
container.setMessageHandler(mock(QueueMessageHandler.class));
container.setBeanName("testContainerName");
container.afterPropertiesSet();
ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor) container.getTaskExecutor();
assertNotNull(taskExecutor);
assertEquals("testContainerName-", taskExecutor.getThreadNamePrefix());
}
@Test
public void testCustomTaskExecutor() throws Exception {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
container.setTaskExecutor(taskExecutor);
container.setAmazonSqs(mock(AmazonSQSAsync.class, withSettings().stubOnly()));
container.setMessageHandler(mock(QueueMessageHandler.class));
container.setBeanName("testContainerName");
container.afterPropertiesSet();
assertEquals(taskExecutor, container.getTaskExecutor());
}
@Test
public void testSimpleReceiveMessage() throws Exception {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
final CountDownLatch countDownLatch = new CountDownLatch(1);
QueueMessageHandler messageHandler = new QueueMessageHandler() {
@Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
countDownLatch.countDown();
assertEquals("messageContent", message.getPayload());
}
};
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
messageHandler.setApplicationContext(applicationContext);
container.setBeanName("testContainerName");
messageHandler.afterPropertiesSet();
mockGetQueueUrl(sqs, "testQueue", "http://testSimpleReceiveMessage.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://testSimpleReceiveMessage.amazonaws.com");
container.afterPropertiesSet();
when(sqs.receiveMessage(new ReceiveMessageRequest("http://testSimpleReceiveMessage.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("messageContent"),
new Message().withBody("messageContent")))
.thenReturn(new ReceiveMessageResult());
when(sqs.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(new GetQueueAttributesResult());
container.start();
assertTrue(countDownLatch.await(1, TimeUnit.SECONDS));
container.stop();
}
@Test
public void testContainerDoesNotProcessMessageAfterBeingStopped() throws Exception {
final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
container.setTaskExecutor(taskExecutor);
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler() {
@Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
fail("Should not have been called");
}
};
container.setMessageHandler(messageHandler);
container.setBeanName("testContainerName");
mockGetQueueUrl(sqs, "testQueue", "http://testContainerDoesNotProcessMessageAfterBeingStopped.amazonaws.com");
container.afterPropertiesSet();
when(sqs.receiveMessage(new ReceiveMessageRequest("http://testContainerDoesNotProcessMessageAfterBeingStopped.amazonaws.com"))).
thenAnswer(new Answer<ReceiveMessageResult>() {
@Override
public ReceiveMessageResult answer(InvocationOnMock invocation) throws Throwable {
container.stop();
return new ReceiveMessageResult().withMessages(new Message().withBody("test"));
}
});
container.start();
}
@Test
public void listener_withMultipleMessageHandlers_shouldBeCalled() throws Exception {
final CountDownLatch countDownLatch = new CountDownLatch(2);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler();
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
applicationContext.registerSingleton("anotherTestMessageListener", AnotherTestMessageListener.class);
mockGetQueueUrl(sqs, "testQueue", "http://listener_withMultipleMessageHandlers_shouldBeCalled.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://listener_withMultipleMessageHandlers_shouldBeCalled.amazonaws.com");
mockGetQueueUrl(sqs, "anotherTestQueue", "http://listener_withMultipleMessageHandlers_shouldBeCalled.another.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://listener_withMultipleMessageHandlers_shouldBeCalled.another.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
when(sqs.receiveMessage(new ReceiveMessageRequest("http://listener_withMultipleMessageHandlers_shouldBeCalled.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("messageContent")))
.thenReturn(new ReceiveMessageResult());
when(sqs.receiveMessage(new ReceiveMessageRequest("http://listener_withMultipleMessageHandlers_shouldBeCalled.another.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("anotherMessageContent")))
.thenReturn(new ReceiveMessageResult());
when(sqs.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(new GetQueueAttributesResult());
container.start();
assertTrue(countDownLatch.await(2L, TimeUnit.SECONDS));
container.stop();
assertEquals("messageContent", applicationContext.getBean(TestMessageListener.class).getMessage());
assertEquals("anotherMessageContent", applicationContext.getBean(AnotherTestMessageListener.class).getMessage());
}
@Test
public void messageExecutor_withMessageWithAttributes_shouldPassThemAsHeaders() throws Exception {
// Arrange
final CountDownLatch countDownLatch = new CountDownLatch(1);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = spy(new QueueMessageHandler());
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
mockGetQueueUrl(sqs, "testQueue", "http://messageExecutor_withMessageWithAttributes_shouldPassThemAsHeaders.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://messageExecutor_withMessageWithAttributes_shouldPassThemAsHeaders.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
when(sqs.receiveMessage(new ReceiveMessageRequest("http://messageExecutor_withMessageWithAttributes_shouldPassThemAsHeaders.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("messageContent").withAttributes(Collections.singletonMap("SenderId", "ID"))))
.thenReturn(new ReceiveMessageResult());
when(sqs.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(new GetQueueAttributesResult());
// Act
container.start();
// Assert
assertTrue(countDownLatch.await(2L, TimeUnit.SECONDS));
container.stop();
verify(messageHandler).handleMessage(this.stringMessageCaptor.capture());
assertEquals("ID", this.stringMessageCaptor.getValue().getHeaders().get("SenderId"));
}
@Test
public void doDestroy_whenContainerCallsDestroy_DestroysDefaultTaskExecutor() throws Exception {
// Arrange
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
simpleMessageListenerContainer.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = mock(QueueMessageHandler.class);
simpleMessageListenerContainer.setMessageHandler(messageHandler);
simpleMessageListenerContainer.afterPropertiesSet();
simpleMessageListenerContainer.start();
// Act
simpleMessageListenerContainer.destroy();
// Assert
assertTrue(((ThreadPoolTaskExecutor) simpleMessageListenerContainer.getTaskExecutor()).getThreadPoolExecutor().isTerminated());
}
@Test
public void afterPropertiesSet_whenCalled_taskExecutorIsActive() throws Exception {
// Arrange
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = mock(QueueMessageHandler.class);
container.setMessageHandler(messageHandler);
// Act
container.afterPropertiesSet();
// Assert
assertFalse(((ThreadPoolTaskExecutor) container.getTaskExecutor()).getThreadPoolExecutor().isTerminated());
container.stop();
}
@Test
public void messageExecutor_messageWithMimeTypeMessageAttribute_shouldSetItAsHeader() throws Exception {
// Arrange
final CountDownLatch countDownLatch = new CountDownLatch(1);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = spy(new QueueMessageHandler());
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
mockGetQueueUrl(sqs, "testQueue", "http://messageExecutor_messageWithMimeTypeMessageAttribute_shouldSetItAsHeader.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://messageExecutor_messageWithMimeTypeMessageAttribute_shouldSetItAsHeader.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
MimeType mimeType = new MimeType("text", "plain", Charset.forName("UTF-8"));
when(sqs.receiveMessage(new ReceiveMessageRequest("http://messageExecutor_messageWithMimeTypeMessageAttribute_shouldSetItAsHeader.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("messageContent")
.withAttributes(Collections.singletonMap("SenderId", "ID"))
.withMessageAttributes(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, new MessageAttributeValue().withDataType("String")
.withStringValue(mimeType.toString())))))
.thenReturn(new ReceiveMessageResult());
when(sqs.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(new GetQueueAttributesResult());
// Act
container.start();
// Assert
assertTrue(countDownLatch.await(2L, TimeUnit.SECONDS));
container.stop();
verify(messageHandler).handleMessage(this.stringMessageCaptor.capture());
assertEquals(mimeType, this.stringMessageCaptor.getValue().getHeaders().get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void queueMessageHandler_withJavaConfig_shouldScanTheListenerMethods() throws Exception {
// Arrange & Act
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SqsTestConfig.class, TestMessageListener.class);
SimpleMessageListenerContainer simpleMessageListenerContainer = applicationContext.getBean(SimpleMessageListenerContainer.class);
QueueMessageHandler queueMessageHandler = (QueueMessageHandler) ReflectionTestUtils.getField(simpleMessageListenerContainer, "messageHandler");
// Assert
assertEquals(1, queueMessageHandler.getHandlerMethods().size());
}
@Test
public void executeMessage_successfulExecution_shouldRemoveMessageFromQueue() throws Exception {
// Arrange
final CountDownLatch countDownLatch = new CountDownLatch(1);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class);
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler();
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
mockGetQueueUrl(sqs, "testQueue", "http://executeMessage_successfulExecution_shouldRemoveMessageFromQueue.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://executeMessage_successfulExecution_shouldRemoveMessageFromQueue.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
mockReceiveMessage(sqs, "http://executeMessage_successfulExecution_shouldRemoveMessageFromQueue.amazonaws.com", "messageContent", "ReceiptHandle");
// Act
container.start();
// Assert
assertTrue(countDownLatch.await(2L, TimeUnit.SECONDS));
container.stop();
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://executeMessage_successfulExecution_shouldRemoveMessageFromQueue.amazonaws.com", "ReceiptHandle")));
}
@Test
public void executeMessage_executionThrowsExceptionAndQueueHasAllDeletionPolicy_shouldRemoveMessageFromQueue() throws Exception {
// Arrange
final CountDownLatch countDownLatch = new CountDownLatch(1);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class);
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler();
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListenerThatThrowsAnExceptionWithAllDeletionPolicy.class);
mockGetQueueUrl(sqs, "testQueue", "http://executeMessage_executionThrowsExceptionAndQueueHasAllDeletionPolicy_shouldRemoveMessageFromQueue.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://executeMessage_executionThrowsExceptionAndQueueHasAllDeletionPolicy_shouldRemoveMessageFromQueue.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
when(sqs.receiveMessage(new ReceiveMessageRequest("http://executeMessage_executionThrowsExceptionAndQueueHasAllDeletionPolicy_shouldRemoveMessageFromQueue.amazonaws.com").withAttributeNames("All").withMaxNumberOfMessages(10).withMessageAttributeNames("All"))).
thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("messageContent").withReceiptHandle("ReceiptHandle")),
new ReceiveMessageResult());
// Act
container.start();
// Assert
assertTrue(countDownLatch.await(2L, TimeUnit.SECONDS));
container.stop();
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://executeMessage_executionThrowsExceptionAndQueueHasAllDeletionPolicy_shouldRemoveMessageFromQueue.amazonaws.com", "ReceiptHandle")));
}
@Test
public void executeMessage_executionThrowsExceptionAndQueueHasRedrivePolicy_shouldNotRemoveMessageFromQueue() throws Exception {
// Arrange
final CountDownLatch countDownLatch = new CountDownLatch(1);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class);
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler();
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListenerThatThrowsAnExceptionWithAllExceptOnRedriveDeletionPolicy.class);
mockGetQueueUrl(sqs, "testQueue", "http://executeMessage_executionThrowsExceptionAndQueueHasRedrivePolicy_shouldNotRemoveMessageFromQueue.amazonaws.com");
mockGetQueueAttributesWithRedrivePolicy(sqs, "http://executeMessage_executionThrowsExceptionAndQueueHasRedrivePolicy_shouldNotRemoveMessageFromQueue.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
when(sqs.receiveMessage(new ReceiveMessageRequest("http://executeMessage_executionThrowsExceptionAndQueueHasRedrivePolicy_shouldNotRemoveMessageFromQueue.amazonaws.com").withAttributeNames("All")
.withMaxNumberOfMessages(10)
.withMessageAttributeNames("All")))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody("messageContent").withReceiptHandle("ReceiptHandle")),
new ReceiveMessageResult());
// Act
container.start();
// Assert
assertTrue(countDownLatch.await(2L, TimeUnit.SECONDS));
container.stop();
verify(sqs, never()).deleteMessageAsync(eq(new DeleteMessageRequest("http://executeMessage_executionThrowsExceptionAndQueueHasRedrivePolicy_shouldNotRemoveMessageFromQueue.amazonaws.com", "ReceiptHandle")));
}
@Test
public void doStop_containerNotRunning_shouldNotThrowAnException() throws Exception {
// Arrange
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setAmazonSqs(mock(AmazonSQSAsync.class, withSettings().stubOnly()));
container.setMessageHandler(mock(QueueMessageHandler.class));
container.setAutoStartup(false);
container.afterPropertiesSet();
// Act & Assert
container.stop();
}
@Test
public void receiveMessage_throwsAnException_operationShouldBeRetried() throws Exception {
// Arrange
Level previous = disableLogging();
AmazonSQSAsync amazonSqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
when(amazonSqs.receiveMessage(any(ReceiveMessageRequest.class))).thenThrow(new RuntimeException("Boom!"))
.thenReturn(new ReceiveMessageResult()
.withMessages(new Message().withBody("messageContent"),
new Message().withBody("messageContent")));
final CountDownLatch countDownLatch = new CountDownLatch(1);
QueueMessageHandler messageHandler = new QueueMessageHandler() {
@Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
countDownLatch.countDown();
assertEquals("messageContent", message.getPayload());
}
};
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
messageHandler.setApplicationContext(applicationContext);
mockGetQueueUrl(amazonSqs, "testQueue", "http://receiveMessage_throwsAnException_operationShouldBeRetried.amazonaws.com");
messageHandler.afterPropertiesSet();
when(amazonSqs.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(new GetQueueAttributesResult());
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setBackOffTime(0);
container.setAmazonSqs(amazonSqs);
container.setMessageHandler(messageHandler);
container.setAutoStartup(false);
container.afterPropertiesSet();
// Act
container.start();
// Assert
assertTrue(countDownLatch.await(1, TimeUnit.SECONDS));
container.stop();
setLogLevel(previous);
}
@Test
public void receiveMessage_withMessageListenerMethodAndNeverDeletionPolicy_waitsForAcknowledgmentBeforeDeletion() throws Exception {
// Arrange
final CountDownLatch countDownLatch = new CountDownLatch(1);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
protected void executeMessage(org.springframework.messaging.Message<String> stringMessage) {
countDownLatch.countDown();
super.executeMessage(stringMessage);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class);
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler();
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testListener", TestMessageListenerWithManualDeletionPolicy.class);
mockGetQueueUrl(sqs, "testQueue", "http://receiveMessage_withMessageListenerMethodAndNeverDeletionPolicy_waitsForAcknowledgmentBeforeDeletion.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://receiveMessage_withMessageListenerMethodAndNeverDeletionPolicy_waitsForAcknowledgmentBeforeDeletion.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
mockReceiveMessage(sqs, "http://receiveMessage_withMessageListenerMethodAndNeverDeletionPolicy_waitsForAcknowledgmentBeforeDeletion.amazonaws.com", "messageContent", "ReceiptHandle");
// Act
container.start();
// Assert
countDownLatch.await(1L, TimeUnit.SECONDS);
verify(sqs, never()).deleteMessageAsync(eq(new DeleteMessageRequest("http://receiveMessage_withMessageListenerMethodAndNeverDeletionPolicy_waitsForAcknowledgmentBeforeDeletion.amazonaws.com", "ReceiptHandle")));
TestMessageListenerWithManualDeletionPolicy testMessageListenerWithManualDeletionPolicy = applicationContext.getBean(TestMessageListenerWithManualDeletionPolicy.class);
testMessageListenerWithManualDeletionPolicy.getCountDownLatch().await(1L, TimeUnit.SECONDS);
testMessageListenerWithManualDeletionPolicy.acknowledge();
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://receiveMessage_withMessageListenerMethodAndNeverDeletionPolicy_waitsForAcknowledgmentBeforeDeletion.amazonaws.com", "ReceiptHandle")));
container.stop();
}
@Test
public void executeMessage_withDifferentDeletionPolicies_shouldActAccordingly() throws Exception {
// Arrange
Level previous = disableLogging();
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class);
container.setAmazonSqs(sqs);
QueueMessageHandler messageHandler = new QueueMessageHandler();
container.setMessageHandler(messageHandler);
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testListener", TestMessageListenerWithAllPossibleDeletionPolicies.class);
mockGetQueueUrl(sqs, "alwaysSuccess", "http://alwaysSuccess.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://alwaysSuccess.amazonaws.com");
mockGetQueueUrl(sqs, "alwaysError", "http://alwaysError.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://alwaysError.amazonaws.com");
mockGetQueueUrl(sqs, "onSuccessSuccess", "http://onSuccessSuccess.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://onSuccessSuccess.amazonaws.com");
mockGetQueueUrl(sqs, "onSuccessError", "http://onSuccessError.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://onSuccessError.amazonaws.com");
mockGetQueueUrl(sqs, "noRedriveSuccess", "http://noRedriveSuccess.amazonaws.com");
mockGetQueueAttributesWithRedrivePolicy(sqs, "http://noRedriveSuccess.amazonaws.com");
mockGetQueueUrl(sqs, "noRedriveError", "http://noRedriveError.amazonaws.com");
mockGetQueueAttributesWithRedrivePolicy(sqs, "http://noRedriveError.amazonaws.com");
mockGetQueueUrl(sqs, "neverSuccess", "http://neverSuccess.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://neverSuccess.amazonaws.com");
mockGetQueueUrl(sqs, "neverError", "http://neverError.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://neverError.amazonaws.com");
messageHandler.setApplicationContext(applicationContext);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
mockReceiveMessage(sqs, "http://alwaysSuccess.amazonaws.com", "foo", "alwaysSuccess");
mockReceiveMessage(sqs, "http://alwaysError.amazonaws.com", "foo", "alwaysError");
mockReceiveMessage(sqs, "http://onSuccessSuccess.amazonaws.com", "foo", "onSuccessSuccess");
mockReceiveMessage(sqs, "http://onSuccessError.amazonaws.com", "foo", "onSuccessError");
mockReceiveMessage(sqs, "http://noRedriveSuccess.amazonaws.com", "foo", "noRedriveSuccess");
mockReceiveMessage(sqs, "http://noRedriveError.amazonaws.com", "foo", "noRedriveError");
mockReceiveMessage(sqs, "http://neverSuccess.amazonaws.com", "foo", "neverSuccess");
mockReceiveMessage(sqs, "http://neverError.amazonaws.com", "foo", "neverError");
// Act
container.start();
// Assert
TestMessageListenerWithAllPossibleDeletionPolicies bean = applicationContext.getBean(TestMessageListenerWithAllPossibleDeletionPolicies.class);
assertTrue(bean.getCountdownLatch().await(1L, TimeUnit.SECONDS));
container.stop();
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://alwaysSuccess.amazonaws.com", "alwaysSuccess")));
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://alwaysError.amazonaws.com", "alwaysError")));
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://onSuccessSuccess.amazonaws.com", "onSuccessSuccess")));
verify(sqs, never()).deleteMessageAsync(eq(new DeleteMessageRequest("http://onSuccessError.amazonaws.com", "onSuccessError")));
verify(sqs, times(1)).deleteMessageAsync(eq(new DeleteMessageRequest("http://noRedriveSuccess.amazonaws.com", "noRedriveSuccess")));
verify(sqs, never()).deleteMessageAsync(eq(new DeleteMessageRequest("http://noRedriveError.amazonaws.com", "noRedriveError")));
verify(sqs, never()).deleteMessageAsync(eq(new DeleteMessageRequest("http://neverSuccess.amazonaws.com", "neverSuccess")));
verify(sqs, never()).deleteMessageAsync(eq(new DeleteMessageRequest("http://neverError.amazonaws.com", "neverError")));
setLogLevel(previous);
}
private static Level disableLogging() {
Level previous = LogManager.getLogger(SimpleMessageListenerContainer.class).getLevel();
LogManager.getLogger(SimpleMessageListenerContainer.class).setLevel(Level.OFF);
return previous;
}
private static void setLogLevel(Level level) {
LogManager.getLogger(SimpleMessageListenerContainer.class).setLevel(level);
}
@Test
public void stop_withALogicalQueueName_mustStopOnlyTheSpecifiedQueue() throws Exception {
// Arrange
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
applicationContext.registerSingleton("anotherTestMessageListener", AnotherTestMessageListener.class);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
container.setBackOffTime(0);
QueueMessageHandler messageHandler = new QueueMessageHandler();
messageHandler.setApplicationContext(applicationContext);
container.setMessageHandler(messageHandler);
mockGetQueueUrl(sqs, "testQueue", "http://stop_withALogicalQueueName_mustStopOnlyTheSpecifiedQueue.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://stop_withALogicalQueueName_mustStopOnlyTheSpecifiedQueue.amazonaws.com");
mockGetQueueUrl(sqs, "anotherTestQueue", "http://stop_withALogicalQueueName_mustStopOnlyTheSpecifiedQueue.another.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://stop_withALogicalQueueName_mustStopOnlyTheSpecifiedQueue.another.amazonaws.com");
when(sqs.receiveMessage(any(ReceiveMessageRequest.class))).thenReturn(new ReceiveMessageResult());
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
container.start();
assertTrue(container.isRunning("testQueue"));
assertTrue(container.isRunning("anotherTestQueue"));
// Act
container.stop("testQueue");
// Assert
assertFalse(container.isRunning("testQueue"));
assertTrue(container.isRunning("anotherTestQueue"));
container.stop();
assertFalse(container.isRunning("testQueue"));
assertFalse(container.isRunning("anotherTestQueue"));
}
@Test
public void stopAndStart_withContainerHavingARunningQueue_shouldRestartTheSpecifiedQueue() throws Exception {
// Arrange
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
applicationContext.registerSingleton("anotherTestMessageListener", AnotherTestMessageListener.class);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
MockAmazonSqsAsyncClient sqs = new MockAmazonSqsAsyncClient();
container.setAmazonSqs(sqs);
container.setBackOffTime(0);
QueueMessageHandler messageHandler = new QueueMessageHandler();
messageHandler.setApplicationContext(applicationContext);
container.setMessageHandler(messageHandler);
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
container.start();
container.stop("testQueue");
assertFalse(container.isRunning("testQueue"));
assertTrue(container.isRunning("anotherTestQueue"));
sqs.setReceiveMessageEnabled(true);
// Act
container.start("testQueue");
// Assert
assertTrue(container.isRunning("testQueue"));
assertTrue(container.isRunning("anotherTestQueue"));
TestMessageListener testMessageListener = applicationContext.getBean(TestMessageListener.class);
boolean await = testMessageListener.getCountDownLatch().await(1, TimeUnit.SECONDS);
assertTrue(await);
assertEquals("Hello", testMessageListener.getMessage());
container.stop();
assertFalse(container.isRunning("testQueue"));
assertFalse(container.isRunning("anotherTestQueue"));
}
@Test
public void stop_withQueueNameThatDoesNotExist_throwsAnException() throws Exception {
// Arrange
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setAmazonSqs(mock(AmazonSQSAsync.class, withSettings().stubOnly()));
container.setMessageHandler(new QueueMessageHandler());
container.afterPropertiesSet();
this.expectedException.expect(IllegalArgumentException.class);
this.expectedException.expectMessage("foo");
// Act
container.stop("foo");
}
@Test
public void start_withQueueNameThatDoesNotExist_throwAnException() throws Exception {
// Arrange
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setAmazonSqs(mock(AmazonSQSAsync.class, withSettings().stubOnly()));
container.setMessageHandler(new QueueMessageHandler());
container.afterPropertiesSet();
this.expectedException.expect(IllegalArgumentException.class);
this.expectedException.expectMessage("bar");
// Act
container.start("bar");
}
@Test
public void start_withAQueueNameThatIsAlreadyRunning_shouldNotStartTheQueueAgainAndIgnoreTheCall() throws Exception {
// Arrange
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
when(sqs.receiveMessage(any(ReceiveMessageRequest.class))).thenReturn(new ReceiveMessageResult());
container.setAmazonSqs(sqs);
container.setBackOffTime(0);
QueueMessageHandler messageHandler = new QueueMessageHandler();
messageHandler.setApplicationContext(applicationContext);
container.setMessageHandler(messageHandler);
mockGetQueueUrl(sqs, "testQueue", "http://start_withAQueueNameThatIsAlreadyRunning_shouldNotStartTheQueueAgainAndIgnoreTheCall.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://start_withAQueueNameThatIsAlreadyRunning_shouldNotStartTheQueueAgainAndIgnoreTheCall.amazonaws.com");
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
container.start();
assertTrue(container.isRunning("testQueue"));
// Act
container.start("testQueue");
// Assert
assertTrue(container.isRunning("testQueue"));
container.stop();
}
@Test
public void stop_withAQueueNameThatIsNotRunning_shouldNotStopTheQueueAgainAndIgnoreTheCall() throws Exception {
// Arrange
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
container.setBackOffTime(0);
QueueMessageHandler messageHandler = new QueueMessageHandler();
messageHandler.setApplicationContext(applicationContext);
container.setMessageHandler(messageHandler);
mockGetQueueUrl(sqs, "testQueue", "http://stop_withAQueueNameThatIsNotRunning_shouldNotStopTheQueueAgainAndIgnoreTheCall.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://stop_withAQueueNameThatIsNotRunning_shouldNotStopTheQueueAgainAndIgnoreTheCall.amazonaws.com");
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
container.start();
container.stop("testQueue");
assertFalse(container.isRunning("testQueue"));
// Act
container.stop("testQueue");
// Assert
assertFalse(container.isRunning("testQueue"));
}
@Test
public void setQueueStopTimeout_withNotDefaultTimeout_mustBeUsedWhenStoppingAQueue() throws Exception {
// Arrange
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("longRunningListenerMethod", LongRunningListenerMethod.class);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
container.setBackOffTime(0);
container.setQueueStopTimeout(100);
QueueMessageHandler messageHandler = new QueueMessageHandler();
messageHandler.setApplicationContext(applicationContext);
container.setMessageHandler(messageHandler);
mockGetQueueUrl(sqs, "longRunningQueueMessage", "http://setQueueStopTimeout_withNotDefaultTimeout_mustBeUsedWhenStoppingAQueue.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://setQueueStopTimeout_withNotDefaultTimeout_mustBeUsedWhenStoppingAQueue.amazonaws.com");
mockReceiveMessage(sqs, "http://setQueueStopTimeout_withNotDefaultTimeout_mustBeUsedWhenStoppingAQueue.amazonaws.com", "Hello", "ReceiptHandle");
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
container.start();
applicationContext.getBean(LongRunningListenerMethod.class).getCountDownLatch().await(1, TimeUnit.SECONDS);
StopWatch stopWatch = new StopWatch();
// Act
stopWatch.start();
container.stop("longRunningQueueMessage");
stopWatch.stop();
// Assert
assertEquals(100, container.getQueueStopTimeout());
assertTrue("stop must last at least the defined queue stop timeout (> 100ms)", stopWatch.getTotalTimeMillis() >= container.getQueueStopTimeout());
assertTrue("stop must last less than the listener method (< 10000ms)", stopWatch.getTotalTimeMillis() < LongRunningListenerMethod.LISTENER_METHOD_WAIT_TIME);
container.stop();
}
@Test
public void stop_withContainerHavingMultipleQueuesRunning_shouldStopQueuesInParallel() throws Exception {
// Arrange
StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("testMessageListener", TestMessageListener.class);
applicationContext.registerSingleton("anotherTestMessageListener", AnotherTestMessageListener.class);
final CountDownLatch testQueueCountdownLatch = new CountDownLatch(1);
final CountDownLatch anotherTestQueueCountdownLatch = new CountDownLatch(1);
final CountDownLatch spinningThreadsStarted = new CountDownLatch(2);
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer() {
@Override
public void stopQueue(String logicalQueueName) {
if ("testQueue".equals(logicalQueueName)) {
testQueueCountdownLatch.countDown();
} else if ("anotherTestQueue".equals(logicalQueueName)) {
anotherTestQueueCountdownLatch.countDown();
}
super.stopQueue(logicalQueueName);
}
};
AmazonSQSAsync sqs = mock(AmazonSQSAsync.class, withSettings().stubOnly());
container.setAmazonSqs(sqs);
container.setBackOffTime(100);
container.setQueueStopTimeout(5000);
QueueMessageHandler messageHandler = new QueueMessageHandler();
messageHandler.setApplicationContext(applicationContext);
container.setMessageHandler(messageHandler);
mockGetQueueUrl(sqs, "testQueue", "http://testQueue.amazonaws.com");
mockGetQueueUrl(sqs, "anotherTestQueue", "http://anotherTestQueue.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://testQueue.amazonaws.com");
mockGetQueueAttributesWithEmptyResult(sqs, "http://anotherTestQueue.amazonaws.com");
when(sqs.receiveMessage(new ReceiveMessageRequest("http://testQueue.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenAnswer(new Answer<ReceiveMessageResult>() {
@Override
public ReceiveMessageResult answer(InvocationOnMock invocation) throws Throwable {
spinningThreadsStarted.countDown();
testQueueCountdownLatch.await(1, TimeUnit.SECONDS);
throw new OverLimitException("Boom");
}
});
when(sqs.receiveMessage(new ReceiveMessageRequest("http://anotherTestQueue.amazonaws.com").withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenAnswer(new Answer<ReceiveMessageResult>() {
@Override
public ReceiveMessageResult answer(InvocationOnMock invocation) throws Throwable {
spinningThreadsStarted.countDown();
anotherTestQueueCountdownLatch.await(1, TimeUnit.SECONDS);
throw new OverLimitException("Boom");
}
});
messageHandler.afterPropertiesSet();
container.afterPropertiesSet();
container.start();
spinningThreadsStarted.await(1, TimeUnit.SECONDS);
StopWatch stopWatch = new StopWatch();
// Act
stopWatch.start();
container.stop();
stopWatch.stop();
// Assert
assertTrue("Stop time must be shorter than stopping one queue after the other", stopWatch.getTotalTimeMillis() < 200);
}
// This class is needed because it does not seem to work when using mockito to mock those requests
private static class MockAmazonSqsAsyncClient extends AmazonSQSBufferedAsyncClient {
private volatile boolean receiveMessageEnabled;
private MockAmazonSqsAsyncClient() {
super(null);
}
@Override
public GetQueueUrlResult getQueueUrl(GetQueueUrlRequest getQueueUrlRequest) throws AmazonClientException {
return new GetQueueUrlResult().withQueueUrl("http://" + getQueueUrlRequest.getQueueName() + ".amazonaws.com");
}
@Override
public GetQueueAttributesResult getQueueAttributes(GetQueueAttributesRequest getQueueAttributesRequest) throws AmazonClientException {
return new GetQueueAttributesResult();
}
@Override
public ReceiveMessageResult receiveMessage(ReceiveMessageRequest receiveMessageRequest) throws AmazonClientException {
if ("http://testQueue.amazonaws.com".equals(receiveMessageRequest.getQueueUrl()) && this.receiveMessageEnabled) {
return new ReceiveMessageResult().withMessages(new Message().withBody("Hello").withReceiptHandle("ReceiptHandle"));
} else {
return new ReceiveMessageResult();
}
}
public void setReceiveMessageEnabled(boolean receiveMessageEnabled) {
this.receiveMessageEnabled = receiveMessageEnabled;
}
}
private static void mockReceiveMessage(AmazonSQSAsync sqs, String queueUrl, String messageContent, String receiptHandle) {
when(sqs.receiveMessage(new ReceiveMessageRequest(queueUrl).withAttributeNames("All")
.withMessageAttributeNames("All")
.withMaxNumberOfMessages(10)))
.thenReturn(new ReceiveMessageResult().withMessages(new Message().withBody(messageContent).withReceiptHandle(receiptHandle)),
new ReceiveMessageResult());
}
private static void mockGetQueueAttributesWithRedrivePolicy(AmazonSQSAsync sqs, String queueUrl) {
when(sqs.getQueueAttributes(new GetQueueAttributesRequest(queueUrl).withAttributeNames(QueueAttributeName.RedrivePolicy))).
thenReturn(new GetQueueAttributesResult().addAttributesEntry(QueueAttributeName.RedrivePolicy.toString(), "{\"some\": \"JSON\"}"));
}
private static void mockGetQueueAttributesWithEmptyResult(AmazonSQSAsync sqs, String queueUrl) {
when(sqs.getQueueAttributes(new GetQueueAttributesRequest(queueUrl).withAttributeNames(QueueAttributeName.RedrivePolicy))).
thenReturn(new GetQueueAttributesResult());
}
private static void mockGetQueueUrl(AmazonSQSAsync sqs, String queueName, String queueUrl) {
when(sqs.getQueueUrl(new GetQueueUrlRequest(queueName))).thenReturn(new GetQueueUrlResult().
withQueueUrl(queueUrl));
}
private static class TestMessageListener {
private String message;
private final CountDownLatch countDownLatch = new CountDownLatch(1);
@RuntimeUse
@SqsListener("testQueue")
private void handleMessage(String message) {
this.message = message;
this.countDownLatch.countDown();
}
public String getMessage() {
return this.message;
}
public CountDownLatch getCountDownLatch() {
return this.countDownLatch;
}
}
private static class AnotherTestMessageListener {
private String message;
@RuntimeUse
@SqsListener("anotherTestQueue")
private void handleMessage(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
@SuppressWarnings("NonExceptionNameEndsWithException")
private static class TestMessageListenerThatThrowsAnExceptionWithAllDeletionPolicy {
@SuppressWarnings("UnusedDeclaration")
@SqsListener("testQueue")
private void handleMessage(String message) {
throw new RuntimeException();
}
@RuntimeUse
@MessageExceptionHandler(RuntimeException.class)
public void handle() {
// Empty body just to avoid unnecessary log output because no exception handler was found.
}
}
@SuppressWarnings("NonExceptionNameEndsWithException")
private static class TestMessageListenerThatThrowsAnExceptionWithAllExceptOnRedriveDeletionPolicy {
@SuppressWarnings("UnusedDeclaration")
@SqsListener(value = "testQueue", deletionPolicy = SqsMessageDeletionPolicy.NO_REDRIVE)
private void handleMessage(String message) {
throw new RuntimeException();
}
@RuntimeUse
@MessageExceptionHandler(RuntimeException.class)
public void handle() {
// Empty body just to avoid unnecessary log output because no exception handler was found.
}
}
@Configuration
@EnableSqs
protected static class SqsTestConfig {
@Bean
public AmazonSQSAsync amazonSQS() {
AmazonSQSAsync mockAmazonSQS = mock(AmazonSQSAsync.class, withSettings().stubOnly());
mockGetQueueUrl(mockAmazonSQS, "testQueue", "http://testQueue.amazonaws.com");
when(mockAmazonSQS.receiveMessage(any(ReceiveMessageRequest.class))).thenReturn(new ReceiveMessageResult());
when(mockAmazonSQS.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(new GetQueueAttributesResult());
return mockAmazonSQS;
}
}
private static class TestMessageListenerWithManualDeletionPolicy {
private Acknowledgment acknowledgment;
private final CountDownLatch countDownLatch = new CountDownLatch(1);
@RuntimeUse
@SqsListener(value = "testQueue", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
private void manualSuccess(String message, Acknowledgment acknowledgment) {
this.acknowledgment = acknowledgment;
this.countDownLatch.countDown();
}
public void acknowledge() {
this.acknowledgment.acknowledge();
}
public CountDownLatch getCountDownLatch() {
return this.countDownLatch;
}
}
private static class TestMessageListenerWithAllPossibleDeletionPolicies {
private final CountDownLatch countdownLatch = new CountDownLatch(8);
@RuntimeUse
@SqsListener(value = "alwaysSuccess", deletionPolicy = SqsMessageDeletionPolicy.ALWAYS)
private void alwaysSuccess(String message) {
this.countdownLatch.countDown();
}
@RuntimeUse
@SqsListener(value = "alwaysError", deletionPolicy = SqsMessageDeletionPolicy.ALWAYS)
private void alwaysError(String message) {
this.countdownLatch.countDown();
throw new RuntimeException("BOOM!");
}
@RuntimeUse
@SqsListener(value = "onSuccessSuccess", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
private void onSuccessSuccess(String message) {
this.countdownLatch.countDown();
}
@RuntimeUse
@SqsListener(value = "onSuccessError", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
private void onSuccessError(String message) {
this.countdownLatch.countDown();
throw new RuntimeException("BOOM!");
}
@RuntimeUse
@SqsListener(value = "noRedriveSuccess", deletionPolicy = SqsMessageDeletionPolicy.NO_REDRIVE)
private void noRedriveSuccess(String message) {
this.countdownLatch.countDown();
}
@RuntimeUse
@SqsListener(value = "noRedriveError", deletionPolicy = SqsMessageDeletionPolicy.NO_REDRIVE)
private void noRedriveError(String message) {
this.countdownLatch.countDown();
throw new RuntimeException("BOOM!");
}
@RuntimeUse
@SqsListener(value = "neverSuccess", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
private void neverSuccess(String message, Acknowledgment acknowledgment) {
this.countdownLatch.countDown();
}
@RuntimeUse
@SqsListener(value = "neverError", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
private void neverError(String message, Acknowledgment acknowledgment) {
this.countdownLatch.countDown();
throw new RuntimeException("BOOM!");
}
public CountDownLatch getCountdownLatch() {
return this.countdownLatch;
}
@RuntimeUse
@MessageExceptionHandler(RuntimeException.class)
private void swallowExceptions() {
}
}
private static class LongRunningListenerMethod {
private final CountDownLatch countDownLatch = new CountDownLatch(1);
private static final int LISTENER_METHOD_WAIT_TIME = 10000;
@RuntimeUse
@SqsListener("longRunningQueueMessage")
private void handleMessage(String message) throws InterruptedException {
this.countDownLatch.countDown();
Thread.sleep(LISTENER_METHOD_WAIT_TIME);
}
public CountDownLatch getCountDownLatch() {
return this.countDownLatch;
}
}
}