INTEXT-147: Add SnsInboundChannelAdapter

JIRA: https://jira.spring.io/browse/INTEXT-147

INTEXT-147: Documentation

Upgrade dependencies
This commit is contained in:
Artem Bilan
2015-03-13 18:20:31 +02:00
parent a7e5f67d04
commit 16ef806598
19 changed files with 915 additions and 27 deletions

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd">
<bean id="amazonSns" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.amazonaws.services.sns.AmazonSNS"/>
</bean>
<int-aws:sns-inbound-channel-adapter sns="amazonSns"
path="/foo"
auto-startup="false"
channel="errorChannel"
error-channel="nullChannel"
phase="100"
id="snsInboundChannelAdapter"
send-timeout="2000"
handle-notification-status="true"
payload-expression="payload.Message"/>
</beans>

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2015 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.integration.aws.config.xml;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer;
import org.springframework.integration.aws.inbound.SnsInboundChannelAdapter;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.amazonaws.services.sns.AmazonSNS;
/**
* @author Artem Bilan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SnsInboundChannelAdapterParserTests {
@Autowired
private AmazonSNS amazonSns;
@Autowired
private MessageChannel errorChannel;
@Autowired
private NullChannel nullChannel;
@Autowired
@Qualifier("snsInboundChannelAdapter")
private SnsInboundChannelAdapter snsInboundChannelAdapter;
@Test
public void testSqsMessageDrivenChannelAdapterParser() {
assertSame(this.amazonSns, TestUtils.getPropertyValue(this.snsInboundChannelAdapter,
"notificationStatusResolver.amazonSns"));
assertTrue(TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "handleNotificationStatus", Boolean.class));
assertArrayEquals(new String[] {"/foo"},
TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "requestMapping.pathPatterns", String[].class));
assertEquals("payload.Message",
TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "payloadExpression.expression"));
assertFalse(this.snsInboundChannelAdapter.isRunning());
assertEquals(100, this.snsInboundChannelAdapter.getPhase());
assertFalse(this.snsInboundChannelAdapter.isAutoStartup());
assertSame(this.errorChannel, TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "requestChannel"));
assertSame(this.nullChannel, TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "errorChannel"));
assertEquals(2000L, TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "messagingTemplate.sendTimeout"));
}
}

View File

@@ -5,7 +5,7 @@
xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging-1.0.xsd">
http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging.xsd">
<aws-messaging:sqs-async-client id="sqs"/>

View File

@@ -5,7 +5,7 @@
xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws.xsd
http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging-1.0.xsd">
http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging.xsd">
<aws-messaging:sqs-async-client id="sqs"/>

View File

@@ -0,0 +1,181 @@
/*
* Copyright 2015 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.integration.aws.inbound;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.messaging.endpoint.NotificationStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.integration.aws.support.AwsHeaders;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.StreamUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.WebApplicationContext;
import com.amazonaws.services.sns.AmazonSNS;
/**
* @author Artem Bilan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
@DirtiesContext
public class SnsInboundChannelAdapterTests {
@Autowired
private WebApplicationContext context;
@Autowired
private AmazonSNS amazonSns;
@Autowired
private PollableChannel inputChannel;
@Value("classpath:org/springframework/integration/aws/inbound/subscriptionConfirmation.json")
private Resource subscriptionConfirmation;
@Value("classpath:org/springframework/integration/aws/inbound/notificationMessage.json")
private Resource notificationMessage;
@Value("classpath:org/springframework/integration/aws/inbound/unsubscribeConfirmation.json")
private Resource unsubscribeConfirmation;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void testSubscriptionConfirmation() throws Exception {
this.mockMvc.perform(
post("/mySampleTopic")
.header("x-amz-sns-message-type", "SubscriptionConfirmation")
.contentType(MediaType.APPLICATION_JSON)
.content(StreamUtils.copyToByteArray(this.subscriptionConfirmation.getInputStream())))
.andExpect(status().isNoContent());
Message<?> receive = inputChannel.receive(10000);
assertNotNull(receive);
assertTrue(receive.getHeaders().containsKey(AwsHeaders.SNS_MESSAGE_TYPE));
assertEquals("SubscriptionConfirmation", receive.getHeaders().get(AwsHeaders.SNS_MESSAGE_TYPE));
assertTrue(receive.getHeaders().containsKey(AwsHeaders.NOTIFICATION_STATUS));
NotificationStatus notificationStatus = (NotificationStatus) receive.getHeaders()
.get(AwsHeaders.NOTIFICATION_STATUS);
notificationStatus.confirmSubscription();
verify(this.amazonSns)
.confirmSubscription("arn:aws:sns:eu-west-1:111111111111:mySampleTopic", "111");
}
@Test
@SuppressWarnings("unchecked")
public void testNotification() throws Exception {
this.mockMvc.perform(
post("/mySampleTopic")
.header("x-amz-sns-message-type", "Notification")
.contentType(MediaType.APPLICATION_JSON)
.content(StreamUtils.copyToByteArray(this.notificationMessage.getInputStream())))
.andExpect(status().isNoContent());
Message<?> receive = inputChannel.receive(10000);
assertNotNull(receive);
Map<String, String> payload = (Map<String, String>) receive.getPayload();
assertEquals("foo", payload.get("Subject"));
assertEquals("bar", payload.get("Message"));
}
@Test
public void testUnsubscribe() throws Exception {
this.mockMvc.perform(
post("/mySampleTopic")
.header("x-amz-sns-message-type", "UnsubscribeConfirmation")
.contentType(MediaType.APPLICATION_JSON)
.content(StreamUtils.copyToByteArray(this.unsubscribeConfirmation.getInputStream())))
.andExpect(status().isNoContent());
Message<?> receive = inputChannel.receive(10000);
assertNotNull(receive);
assertTrue(receive.getHeaders().containsKey(AwsHeaders.SNS_MESSAGE_TYPE));
assertEquals("UnsubscribeConfirmation", receive.getHeaders().get(AwsHeaders.SNS_MESSAGE_TYPE));
assertTrue(receive.getHeaders().containsKey(AwsHeaders.NOTIFICATION_STATUS));
NotificationStatus notificationStatus = (NotificationStatus) receive.getHeaders()
.get(AwsHeaders.NOTIFICATION_STATUS);
notificationStatus.confirmSubscription();
verify(this.amazonSns)
.confirmSubscription("arn:aws:sns:eu-west-1:111111111111:mySampleTopic", "233");
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public AmazonSNS amazonSns() {
return Mockito.mock(AmazonSNS.class);
}
@Bean
public PollableChannel inputChannel() {
return new QueueChannel();
}
@Bean
public HttpRequestHandler sqsMessageDrivenChannelAdapter() {
SnsInboundChannelAdapter adapter = new SnsInboundChannelAdapter(amazonSns(), "/mySampleTopic");
adapter.setRequestChannel(inputChannel());
adapter.setHandleNotificationStatus(true);
return adapter;
}
}
}

View File

@@ -0,0 +1,26 @@
{
"Type": "Notification",
"MessageId": "f2c15fec-c617-5b08-b54d-13c4099fec60",
"TopicArn": "arn:aws:sns:eu-west-1:111111111111:mySampleTopic",
"Subject": "foo",
"Message": "bar",
"Timestamp": "2014-06-28T14:12:24.418Z",
"SignatureVersion": "1",
"Signature": "XDvKSAnhxECrAmyIrs0Dsfbp/tnKD1IvoOOYTU28FtbUoxr/CgziuW87yZwTuSNNbHJbdD3BEjHS0vKewm0xBeQ0PToDkgtoORXo5RWnmShDQ2nhkthFhZnNulKtmFtRogjBtCwbz8sPnbOCSk21ruyXNdV2RUbdDalndAW002CWEQmYMxFSN6OXUtMueuT610aX+tqeYP4Z6+8WTWLWjAuVyy7rOI6KHYBcVDhKtskvTOPZ4tiVohtQdQbO2Gjuh1vblRzzwMkfaoFTSWImd4pFXxEsv/fq9aGIlqq9xEryJ0w2huFwI5gxyhvGt0RnTd9YvmAEC+WzdJDOqaDNxg==",
"SigningCertURL": "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-e372f8ca30337fdb084e8ac449342c77.pem",
"UnsubscribeURL": "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:721324560415:mySampleTopic:9859a6c9-6083-4690-ab02-d1aead3442df",
"MessageAttributes": {
"AWS.SNS.MOBILE.MPNS.Type": {
"Type": "String",
"Value": "token"
},
"AWS.SNS.MOBILE.WNS.Type": {
"Type": "String",
"Value": "wns/badge"
},
"AWS.SNS.MOBILE.MPNS.NotificationClass": {
"Type": "String",
"Value": "realtime"
}
}
}

View File

@@ -0,0 +1,12 @@
{
"Type": "SubscriptionConfirmation",
"MessageId": "e267b24c-5532-472f-889d-c2cdd2143bbc",
"Token": "111",
"TopicArn": "arn:aws:sns:eu-west-1:111111111111:mySampleTopic",
"Message": "You have chosen to subscribe to the topic arn:aws:sns:eu-west-1:721324560415:mySampleTopic.To confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL": "https://sns.eu-west-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:eu-west-1:111111111111:mySampleTopic&Token=111",
"Timestamp": "2014-06-28T10:22:18.086Z",
"SignatureVersion": "1",
"Signature": "JLdRUR+uhP4cyVW6bRuUSAkUosFMJyO7g7WCAwEUJoB4y8vQE1uDUWGpbQSEbruVTjPEM8hFsf4/95NftfM0W5IgND1uSnv4P/4AYyL+q0bLOJlquzXrw4w2NX3QShS3y+r/gXzo7p/UP4NOr35MGCEGPqHAEe1Coc5S0eaP3JvKU6xY1tcop6ze2RNHTwzhM43dda2bnjPYogAJzA5uHfmSjs3cMVvPCckj3zdLyvxISp+RgrogdvlNyu9ycND1SxagmbzjkBaqvF/4aiSYFxsEXX4e9zuNuHGmXGWgm1ppYUGLSPPJruCsPUa7Ii1mYvpX7SezuFZlAAXXBk0mHg==",
"SigningCertURL": "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-e372f8ca30337fdb084e8ac449342c77.pem"
}

View File

@@ -0,0 +1,12 @@
{
"Type": "UnsubscribeConfirmation",
"MessageId": "7b9a6321-45d5-461a-bc35-9c2d18ed9dbe",
"Token": "233",
"TopicArn": "arn:aws:sns:eu-west-1:111111111111:mySampleTopic",
"Message": "You have chosen to deactivate subscription arn:aws:sns:eu-west-1:111111111111:mySampleTopic:f64111de-e681-4820-a8be-474f64c1bbf8.\nTo cancel this operation and restore the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL": "https://sns.eu-west-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:eu-west-1:721324560415:mySampleTopic&Token=233",
"Timestamp": "2014-06-28T19:33:00.497Z",
"SignatureVersion": "1",
"Signature": "EAb0k1XoD2h+u4j2GB42wEFCVUFKEaS/2W+p+3wK1GfZDZn4LeDLbkxJ1LYF/A1CYL+sCJ4ZmLFI1axm0V/p+fESkNbKQoQotMgma+PtA6KnmRrKEU8O6nUELqeVPWFAoQ9ZsW9FCAXVDXoPxiqHNTH+tC7mzAsvajyp4aTm/POqkRKBl+A/7dHUqfHGup/FJhLNgTAciBZSloa5EuBKxInJQfoZjy3DU8qKXXhmKKRdyVwOGEuReo/njy4c3Phtn0+logu2PZUKqkGTuJZVbapHmcTq+0MqIh05sevLDmTEfBlsmNThhWIyCza/t68RRlqm9cRLjINSWfrv1Xkrpw==",
"SigningCertURL": "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-e372f8ca30337fdb084e8ac449342c77.pem"
}