INTEXT-8: Add SNS Outbound Channel Adapter

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

Address PR comments
This commit is contained in:
Artem Bilan
2015-09-28 14:38:33 -04:00
parent dc66af65eb
commit 0580642439
17 changed files with 1056 additions and 62 deletions

View File

@@ -61,7 +61,7 @@ public class SnsInboundChannelAdapterParserTests {
@Test
public void testSqsMessageDrivenChannelAdapterParser() {
public void testSnsInboundChannelAdapterParser() {
assertSame(this.amazonSns, TestUtils.getPropertyValue(this.snsInboundChannelAdapter,
"notificationStatusResolver.amazonSns"));
assertTrue(TestUtils.getPropertyValue(this.snsInboundChannelAdapter, "handleNotificationStatus", Boolean.class));

View File

@@ -0,0 +1,33 @@
<?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"
xmlns:int="http://www.springframework.org/schema/integration"
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/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<bean id="amazonSns" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.amazonaws.services.sns.AmazonSNS"/>
</bean>
<int-aws:sns-outbound-channel-adapter id="defaultAdapter" sns="amazonSns">
<int-aws:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice"/>
</int-aws:request-handler-advice-chain>
</int-aws:sns-outbound-channel-adapter>
<int:channel id="notificationChannel"/>
<int-aws:sns-outbound-gateway
id="snsGateway"
sns="amazonSns"
request-channel="notificationChannel"
reply-channel="errorChannel"
topic-arn="foo"
subject="bar"
body-expression="payload.toUpperCase()"
auto-startup="false"
phase="201"/>
</beans>

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2016 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.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
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 SnsOutboundChannelAdapterParserTests {
@Autowired
private AmazonSNS amazonSns;
@Autowired
@Qualifier("defaultAdapter")
private MessageChannel defaultAdapterChannel;
@Autowired
@Qualifier("errorChannel")
private MessageChannel errorChannel;
@Autowired
@Qualifier("defaultAdapter.adapter")
private AbstractEndpoint defaultAdapter;
@Autowired
@Qualifier("defaultAdapter.handler")
private MessageHandler defaultAdapterHandler;
@Autowired
@Qualifier("notificationChannel")
private MessageChannel notificationChannel;
@Autowired
@Qualifier("snsGateway")
private AbstractEndpoint snsGateway;
@Autowired
@Qualifier("snsGateway.handler")
private MessageHandler snsGatewayHandler;
@Test
public void testSnsOutboundChannelAdapterDefaultParser() throws Exception {
Object handler = TestUtils.getPropertyValue(this.defaultAdapter, "handler");
assertFalse(AopUtils.isAopProxy(handler));
assertSame(this.defaultAdapterHandler, handler);
assertThat(TestUtils.getPropertyValue(handler, "adviceChain", List.class).get(0),
instanceOf(RequestHandlerRetryAdvice.class));
assertSame(this.defaultAdapterChannel, TestUtils.getPropertyValue(this.defaultAdapter, "inputChannel"));
assertSame(this.amazonSns, TestUtils.getPropertyValue(this.defaultAdapterHandler, "amazonSns"));
assertNotNull(TestUtils.getPropertyValue(this.defaultAdapterHandler, "evaluationContext"));
assertNull(TestUtils.getPropertyValue(this.defaultAdapterHandler, "topicArnExpression"));
assertNull(TestUtils.getPropertyValue(this.defaultAdapterHandler, "subjectExpression"));
assertNull(TestUtils.getPropertyValue(this.defaultAdapterHandler, "bodyExpression"));
}
@Test
public void testSnsOutboundChannelAdapterParser() {
assertSame(this.notificationChannel, TestUtils.getPropertyValue(this.snsGateway, "inputChannel"));
assertSame(this.snsGatewayHandler, TestUtils.getPropertyValue(this.snsGateway, "handler"));
assertFalse(TestUtils.getPropertyValue(this.snsGateway, "autoStartup", Boolean.class));
assertEquals(new Integer(201), TestUtils.getPropertyValue(this.snsGateway, "phase", Integer.class));
assertTrue(TestUtils.getPropertyValue(this.snsGatewayHandler, "produceReply", Boolean.class));
assertSame(this.errorChannel, TestUtils.getPropertyValue(this.snsGatewayHandler, "outputChannel"));
assertSame(this.amazonSns, TestUtils.getPropertyValue(this.snsGatewayHandler, "amazonSns"));
assertNotNull(TestUtils.getPropertyValue(this.snsGatewayHandler, "evaluationContext"));
assertEquals("foo",
TestUtils.getPropertyValue(this.snsGatewayHandler, "topicArnExpression", Expression.class)
.getExpressionString());
assertEquals("bar",
TestUtils.getPropertyValue(this.snsGatewayHandler, "subjectExpression", Expression.class)
.getExpressionString());
assertEquals("payload.toUpperCase()",
TestUtils.getPropertyValue(this.snsGatewayHandler, "bodyExpression", Expression.class)
.getExpressionString());
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2016 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.outbound;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.integration.aws.support.SnsBodyBuilder;
/**
* @author Artem Bilan
*/
public class SnsMessageBuilderTests {
@Test
public void testSnsMessageBuilder() {
try {
SnsBodyBuilder.withDefault("");
fail("IllegalArgumentException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(IllegalArgumentException.class));
assertThat(e.getMessage(), containsString("defaultMessage must not be empty."));
}
String message = SnsBodyBuilder.withDefault("foo").build();
assertEquals("{\"default\":\"foo\"}", message);
try {
SnsBodyBuilder.withDefault("foo")
.forProtocols("{\"foo\" : \"bar\"}")
.build();
fail("IllegalArgumentException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(IllegalArgumentException.class));
assertThat(e.getMessage(), containsString("protocols must not be empty."));
}
try {
SnsBodyBuilder.withDefault("foo")
.forProtocols("{\"foo\" : \"bar\"}", "")
.build();
fail("IllegalArgumentException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(IllegalArgumentException.class));
assertThat(e.getMessage(), containsString("protocols must not contain empty elements."));
}
message = SnsBodyBuilder.withDefault("foo")
.forProtocols("{\"foo\" : \"bar\"}", "sms")
.build();
assertEquals("{\"default\":\"foo\",\"sms\":\"{\\\"foo\\\" : \\\"bar\\\"}\"}", message);
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2016 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.outbound;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.aws.support.AwsHeaders;
import org.springframework.integration.aws.support.SnsBodyBuilder;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.amazonaws.services.sns.AmazonSNS;
import com.amazonaws.services.sns.model.PublishRequest;
import com.amazonaws.services.sns.model.PublishResult;
/**
* @author Artem Bilan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
public class SnsMessageHandlerTests {
private static SpelExpressionParser PARSER = new SpelExpressionParser();
@Autowired
private MessageChannel sendToSnsChannel;
@Autowired
private AmazonSNS amazonSNS;
@Test
public void testSnsMessageHandler() {
SnsBodyBuilder payload = SnsBodyBuilder.withDefault("foo")
.forProtocols("{\"foo\" : \"bar\"}", "sms");
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload(payload)
.setHeader("topic", "topic")
.setHeader("subject", "subject")
.setReplyChannel(replyChannel)
.build();
this.sendToSnsChannel.send(message);
Message<?> reply = replyChannel.receive(1000);
assertNotNull(reply);
ArgumentCaptor<PublishRequest> captor = ArgumentCaptor.forClass(PublishRequest.class);
verify(this.amazonSNS).publish(captor.capture());
PublishRequest publishRequest = captor.getValue();
assertEquals("json", publishRequest.getMessageStructure());
assertEquals("topic", publishRequest.getTopicArn());
assertEquals("subject", publishRequest.getSubject());
assertEquals("{\"default\":\"foo\",\"sms\":\"{\\\"foo\\\" : \\\"bar\\\"}\"}", publishRequest.getMessage());
assertEquals("111", reply.getHeaders().get(AwsHeaders.SNS_PUBLISHED_MESSAGE_ID));
assertEquals("topic", reply.getHeaders().get(AwsHeaders.TOPIC));
assertSame(publishRequest, reply.getPayload());
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public AmazonSNS amazonSNS() {
AmazonSNS mock = mock(AmazonSNS.class);
doAnswer(new Answer<PublishResult>() {
@Override
public PublishResult answer(InvocationOnMock invocation) throws Throwable {
return new PublishResult().withMessageId("111");
}
}).when(mock).publish(any(PublishRequest.class));
return mock;
}
@Bean
@ServiceActivator(inputChannel = "sendToSnsChannel")
public MessageHandler snsMessageHandler() {
SnsMessageHandler snsMessageHandler = new SnsMessageHandler(amazonSNS(), true);
snsMessageHandler.setTopicArnExpression(PARSER.parseExpression("headers.topic"));
snsMessageHandler.setSubjectExpression(PARSER.parseExpression("headers.subject"));
snsMessageHandler.setBodyExpression(PARSER.parseExpression("payload"));
return snsMessageHandler;
}
}
}