GH-72: Add KinesisLocalRunning and tests

Fixes spring-projects/spring-integration-aws#72

* Document Kinesis Channel Adapters
* Fix some inconsistency in the `KinesisMessageHandler`
* Add integration test against `KinesisLocalRunning` `@Rule`
* Document testing against Kinesalite
This commit is contained in:
Artem Bilan
2017-08-02 20:01:13 -04:00
parent 638d5e6f75
commit c4a3fcdee2
6 changed files with 361 additions and 12 deletions

View File

@@ -38,13 +38,16 @@ import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsyncClientBuilder;
/**
* The {@link TestWatcher} implementation for local Amazon DynamoDB service.
* See https://github.com/mhart/dynalite.
*
* @author Artem Bilan
*
* @since 1.1
*/
public final class DynamoDbRunning extends TestWatcher {
public final class DynamoDbLocalRunning extends TestWatcher {
private static Log logger = LogFactory.getLog(DynamoDbRunning.class);
private static Log logger = LogFactory.getLog(DynamoDbLocalRunning.class);
// Static so that we only test once on failure: speeds up test suite
private static Map<Integer, Boolean> dynamoDbOnline = new HashMap<>();
@@ -53,7 +56,7 @@ public final class DynamoDbRunning extends TestWatcher {
private AmazonDynamoDBAsync amazonDynamoDB;
private DynamoDbRunning(int port) {
private DynamoDbLocalRunning(int port) {
this.port = port;
dynamoDbOnline.put(port, true);
}
@@ -89,8 +92,8 @@ public final class DynamoDbRunning extends TestWatcher {
}
public static DynamoDbRunning isRunning(int port) {
return new DynamoDbRunning(port);
public static DynamoDbLocalRunning isRunning(int port) {
return new DynamoDbLocalRunning(port);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2017 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;
import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeTrue;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.SDKGlobalConfiguration;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.kinesis.AmazonKinesisAsync;
import com.amazonaws.services.kinesis.AmazonKinesisAsyncClientBuilder;
/**
* The {@link TestWatcher} implementation for local Amazon Kinesis service.
* See https://github.com/mhart/kinesalite.
*
* @author Artem Bilan
*
* @since 1.1
*/
public final class KinesisLocalRunning extends TestWatcher {
private static Log logger = LogFactory.getLog(KinesisLocalRunning.class);
// Static so that we only test once on failure: speeds up test suite
private static Map<Integer, Boolean> kinesisOnline = new HashMap<>();
private final int port;
private AmazonKinesisAsync amazonKinesis;
private KinesisLocalRunning(int port) {
this.port = port;
kinesisOnline.put(port, true);
}
public AmazonKinesisAsync getKinesis() {
return this.amazonKinesis;
}
@Override
public Statement apply(Statement base, Description description) {
assumeTrue(kinesisOnline.get(this.port));
String url = "http://localhost:" + this.port;
// See https://github.com/mhart/kinesalite#cbor-protocol-issues-with-the-java-sdk
System.setProperty(SDKGlobalConfiguration.AWS_CBOR_DISABLE_SYSTEM_PROPERTY, "true");
this.amazonKinesis = AmazonKinesisAsyncClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("", "")))
.withClientConfiguration(
new ClientConfiguration()
.withMaxErrorRetry(0)
.withConnectionTimeout(1000))
.withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration(url, Regions.DEFAULT_REGION.getName()))
.build();
try {
this.amazonKinesis.listStreams();
}
catch (SdkClientException e) {
logger.warn("Tests not running because no Kinesis on " + url, e);
assumeNoException(e);
}
return new Statement() {
public void evaluate() throws Throwable {
try {
base.evaluate();
} finally {
System.clearProperty(SDKGlobalConfiguration.AWS_CBOR_DISABLE_SYSTEM_PROPERTY);
}
}
};
}
public static KinesisLocalRunning isRunning(int port) {
return new KinesisLocalRunning(port);
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2017 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.kinesis;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Date;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.aws.KinesisLocalRunning;
import org.springframework.integration.aws.inbound.kinesis.KinesisMessageDrivenChannelAdapter;
import org.springframework.integration.aws.outbound.KinesisMessageHandler;
import org.springframework.integration.aws.support.AwsHeaders;
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.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Artem Bilan
*
* @since 1.1
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class KinesisIntegrationTests {
@ClassRule
public static final KinesisLocalRunning KINESIS_LOCAL_RUNNING = KinesisLocalRunning.isRunning(4567);
private static final String TEST_STREAM = "TestStream";
@Autowired
private MessageChannel kinesisSendChannel;
@Autowired
private PollableChannel kinesisReceiveChannel;
@BeforeClass
public static void setup() {
KINESIS_LOCAL_RUNNING.getKinesis().createStream(TEST_STREAM, 1);
}
@AfterClass
public static void tearDown() {
KINESIS_LOCAL_RUNNING.getKinesis().deleteStream(TEST_STREAM);
}
@Test
public void testKinesisInboundOutbound() {
Date now = new Date();
this.kinesisSendChannel.send(
MessageBuilder.withPayload(now)
.setHeader(AwsHeaders.STREAM, TEST_STREAM)
.build());
Message<?> receive = this.kinesisReceiveChannel.receive(10_000);
assertThat(receive).isNotNull();
assertThat(receive.getPayload()).isEqualTo(now);
}
@Configuration
@EnableIntegration
public static class TestConfiguration {
@Bean
@ServiceActivator(inputChannel = "kinesisSendChannel")
public MessageHandler kinesisMessageHandler() {
KinesisMessageHandler kinesisMessageHandler = new KinesisMessageHandler(KINESIS_LOCAL_RUNNING.getKinesis());
kinesisMessageHandler.setPartitionKey("1");
return kinesisMessageHandler;
}
@Bean
public KinesisMessageDrivenChannelAdapter kinesisInboundChannelChannel() {
KinesisMessageDrivenChannelAdapter adapter =
new KinesisMessageDrivenChannelAdapter(KINESIS_LOCAL_RUNNING.getKinesis(), TEST_STREAM);
adapter.setOutputChannel(kinesisReceiveChannel());
return adapter;
}
@Bean
public PollableChannel kinesisReceiveChannel() {
return new QueueChannel();
}
}
}

View File

@@ -26,7 +26,7 @@ import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.springframework.integration.aws.DynamoDbRunning;
import org.springframework.integration.aws.DynamoDbLocalRunning;
import org.springframework.integration.test.util.TestUtils;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync;
@@ -47,7 +47,7 @@ import com.amazonaws.waiters.WaiterParameters;
public class DynamoDbMetadataStoreTests {
@ClassRule
public static final DynamoDbRunning DYNAMO_DB_RUNNING = DynamoDbRunning.isRunning(4567);
public static final DynamoDbLocalRunning DYNAMO_DB_RUNNING = DynamoDbLocalRunning.isRunning(4567);
private static final String TEST_TABLE = "testMetadataStore";