parentShardIds = lease.getParentShardIds();
40 | for (String parentShardId : parentShardIds) {
41 |
42 | // Throw an exception if the parent shard exists (but the child does not).
43 | // This may be a (rare) race condition between fetching the shard list and Kinesis expiring shards.
44 | if (currentKinesisShardIds.contains(parentShardId)) {
45 | String message =
46 | "Parent shard " + parentShardId + " exists but not the child shard "
47 | + lease.getLeaseKey();
48 | LOG.info(message);
49 | throw new KinesisClientLibIOException(message);
50 | }
51 | }
52 | }
53 |
54 | return isCandidateForCleanup;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/StreamsMultiLangDaemon.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter;
7 |
8 | import java.io.IOException;
9 | import java.util.concurrent.ExecutionException;
10 | import java.util.concurrent.ExecutorService;
11 | import java.util.concurrent.Future;
12 |
13 | import org.apache.commons.logging.Log;
14 | import org.apache.commons.logging.LogFactory;
15 |
16 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.Worker;
17 | import com.amazonaws.services.kinesis.multilang.MultiLangDaemon;
18 | import com.amazonaws.services.kinesis.multilang.MultiLangDaemonConfig;
19 |
20 | /**
21 | * Main app that launches the worker that runs the multi-language record processor.
22 | * Requires a properties file containing configuration for this daemon and the KCL.
23 | *
24 | * This version extends the KCL's MultiLangDaemon to use DynamoDB Streams instead of
25 | * Kinesis.
26 | */
27 | public class StreamsMultiLangDaemon {
28 |
29 | private static final Log LOG = LogFactory.getLog(StreamsMultiLangDaemon.class);
30 |
31 | /**
32 | * @param args Accepts a single argument, that argument is a properties file which provides KCL configuration as
33 | * well as the name of an executable.
34 | */
35 | public static void main(String[] args) {
36 |
37 | if (args.length == 0) {
38 | MultiLangDaemon.printUsage(System.err, "You must provide a properties file");
39 | System.exit(1);
40 | }
41 | MultiLangDaemonConfig config = null;
42 | try {
43 | config = new MultiLangDaemonConfig(args[0]);
44 | } catch (IOException | IllegalArgumentException e) {
45 | MultiLangDaemon.printUsage(System.err, "You must provide a valid properties file");
46 | System.exit(1);
47 | }
48 |
49 | final ExecutorService executorService = config.getExecutorService();
50 | final Worker worker = StreamsWorkerFactory.createDynamoDbStreamsWorker(config.getRecordProcessorFactory(), config.getKinesisClientLibConfiguration(), executorService);
51 |
52 | // Daemon
53 | final MultiLangDaemon daemon = new MultiLangDaemon(worker);
54 |
55 | final Future future = executorService.submit(daemon);
56 | try {
57 | System.exit(future.get());
58 | } catch (InterruptedException | ExecutionException e) {
59 | LOG.error("Encountered an error while running daemon", e);
60 | }
61 | System.exit(1);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/StreamsRecordProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import org.apache.commons.logging.Log;
12 | import org.apache.commons.logging.LogFactory;
13 |
14 | import com.amazonaws.services.dynamodbv2.streamsadapter.model.RecordAdapter;
15 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorCheckpointer;
16 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
17 | import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
18 | import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
19 | import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
20 | import com.amazonaws.services.kinesis.model.Record;
21 |
22 | /**
23 | * This record processor is intended for use with the DynamoDB Streams Adapter for the
24 | * Amazon Kinesis Client Library (KCL). It will retrieve the underlying Streams records
25 | * from the KCL adapter in order to simplify record processing tasks.
26 | */
27 | public abstract class StreamsRecordProcessor implements IRecordProcessor {
28 |
29 | private static final Log LOG = LogFactory.getLog(StreamsRecordProcessor.class);
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public abstract void initialize(InitializationInput initializationInput);
35 |
36 | public void processRecords(ProcessRecordsInput processRecordsInput) {
37 | final List streamsRecords = new ArrayList();
38 | if (processRecordsInput.getRecords() == null) {
39 | LOG.warn("ProcessRecordsInput's list of Records was null. Skipping.");
40 | return;
41 | }
42 | for (Record record : processRecordsInput.getRecords()) {
43 | if (record instanceof RecordAdapter) {
44 | streamsRecords.add(((RecordAdapter) record).getInternalObject());
45 | } else {
46 | // This record processor is not being used with the
47 | // DynamoDB Streams Adapter for Amazon Kinesis Client
48 | // Library, so we cannot retrieve any Streams records.
49 | throw new IllegalArgumentException("Record is not an instance of RecordAdapter");
50 | }
51 | }
52 | processStreamsRecords(streamsRecords, processRecordsInput.getCheckpointer());
53 | }
54 |
55 | /**
56 | * Process data records. The Amazon Kinesis Client Library will invoke this method to deliver data records to the
57 | * application.
58 | * Upon fail over, the new instance will get records with sequence number > checkpoint position
59 | * for each partition key.
60 | *
61 | * @param records Data records to be processed
62 | * @param checkpointer RecordProcessor should use this instance to checkpoint their progress.
63 | */
64 | public abstract void processStreamsRecords(List records, IRecordProcessorCheckpointer checkpointer);
65 |
66 | /**
67 | * {@inheritDoc}
68 | */
69 | public abstract void shutdown(ShutdownInput shutdownInput);
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/exceptions/UnableToReadMoreRecordsException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.exceptions;
7 |
8 | /**
9 | * This exception is thrown when records have been trimmed and the user has specified to not continue processing.
10 | */
11 | public class UnableToReadMoreRecordsException extends RuntimeException {
12 |
13 | private static final long serialVersionUID = 7280447889113524297L;
14 |
15 | public UnableToReadMoreRecordsException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/DescribeStreamRequestAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamRequest;
9 |
10 | /**
11 | * Container for the parameters to the DescribeStream operation.
12 | */
13 | public class DescribeStreamRequestAdapter extends DescribeStreamRequest {
14 |
15 | private com.amazonaws.services.kinesis.model.DescribeStreamRequest internalRequest;
16 |
17 | /**
18 | * Constructs a new request using an Amazon Kinesis object.
19 | *
20 | * @param request Instance of Amazon Kinesis DescribeStreamRequest
21 | */
22 | public DescribeStreamRequestAdapter(com.amazonaws.services.kinesis.model.DescribeStreamRequest request) {
23 | internalRequest = request;
24 | }
25 |
26 | /**
27 | * @return The shard ID of the shard to start with for the stream description.
28 | */
29 | @Override
30 | public String getExclusiveStartShardId() {
31 | return internalRequest.getExclusiveStartShardId();
32 | }
33 |
34 | /**
35 | * @param exclusiveStartShardId The shard ID of the shard to start with for the stream description.
36 | */
37 | @Override
38 | public void setExclusiveStartShardId(String exclusiveStartShardId) {
39 | internalRequest.setExclusiveStartShardId(exclusiveStartShardId);
40 | }
41 |
42 | /**
43 | * @param exclusiveStartShardId The shard ID of the shard to start with for the stream description.
44 | * @return A reference to this updated object so that method calls can be chained together.
45 | */
46 | @Override
47 | public DescribeStreamRequest withExclusiveStartShardId(String exclusiveStartShardId) {
48 | internalRequest.setExclusiveStartShardId(exclusiveStartShardId);
49 | return this;
50 | }
51 |
52 | /**
53 | * @return The maximum number of shards to return.
54 | */
55 | @Override
56 | public Integer getLimit() {
57 | return internalRequest.getLimit();
58 | }
59 |
60 | /**
61 | * @param limit The maximum number of shards to return.
62 | */
63 | @Override
64 | public void setLimit(Integer limit) {
65 | internalRequest.setLimit(limit);
66 | }
67 |
68 | /**
69 | * @param limit The maximum number of shards to return.
70 | * @return A reference to this updated object so that method calls can be chained together.
71 | */
72 | @Override
73 | public DescribeStreamRequest withLimit(Integer limit) {
74 | internalRequest.setLimit(limit);
75 | return this;
76 | }
77 |
78 | /**
79 | * @return The ARN of the stream to describe.
80 | */
81 | @Override
82 | public String getStreamArn() {
83 | return internalRequest.getStreamName();
84 | }
85 |
86 | /**
87 | * @param streamArn The ARN of the stream to describe.
88 | */
89 | @Override
90 | public void setStreamArn(String streamArn) {
91 | internalRequest.setStreamName(streamArn);
92 | }
93 |
94 | /**
95 | * @param streamArn The ARN of the stream to describe.
96 | * @return A reference to this updated object so that method calls can be chained together.
97 | */
98 | @Override
99 | public DescribeStreamRequest withStreamArn(String streamArn) {
100 | internalRequest.setStreamName(streamArn);
101 | return this;
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/DescribeStreamResultAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.kinesis.model.DescribeStreamResult;
9 | import com.amazonaws.services.kinesis.model.StreamDescription;
10 |
11 | /**
12 | * Represents the output of a DescribeStream operation.
13 | */
14 | public class DescribeStreamResultAdapter extends DescribeStreamResult {
15 |
16 | private StreamDescription streamDescription;
17 |
18 | /**
19 | * Constructs a new result using a DynamoDBStreams object.
20 | *
21 | * @param result Instance of DynamoDBStreams DescribeStreamResult
22 | */
23 | public DescribeStreamResultAdapter(com.amazonaws.services.dynamodbv2.model.DescribeStreamResult result) {
24 | streamDescription = new StreamDescriptionAdapter(result.getStreamDescription());
25 | }
26 |
27 | /**
28 | * @return Contains the current status of the stream, the stream ARN, an array of
29 | * shard objects that comprise the stream, and states whether there are
30 | * more shards available.
31 | */
32 | @Override
33 | public StreamDescription getStreamDescription() {
34 | return streamDescription;
35 | }
36 |
37 | @Override
38 | public void setStreamDescription(StreamDescription streamDescription) {
39 | throw new UnsupportedOperationException();
40 | }
41 |
42 | @Override
43 | public DescribeStreamResult withStreamDescription(StreamDescription streamDescription) {
44 | throw new UnsupportedOperationException();
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetRecordsRequestAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.dynamodbv2.model.GetRecordsRequest;
9 |
10 | /**
11 | * Container for the parameters to the GetRecords operation.
12 | */
13 | public class GetRecordsRequestAdapter extends GetRecordsRequest {
14 |
15 | private com.amazonaws.services.kinesis.model.GetRecordsRequest internalRequest;
16 |
17 | /**
18 | * Constructs a new request from an Amazon Kinesis object.
19 | *
20 | * @param request Instance of AmazonKinesis GetRecordsReqest
21 | */
22 | public GetRecordsRequestAdapter(com.amazonaws.services.kinesis.model.GetRecordsRequest request) {
23 | internalRequest = request;
24 | }
25 |
26 | /**
27 | * @return The maximum number of records to return.
28 | */
29 | @Override
30 | public Integer getLimit() {
31 | return internalRequest.getLimit();
32 | }
33 |
34 | /**
35 | * @param limit The maximum number of records to return.
36 | */
37 | @Override
38 | public void setLimit(Integer limit) {
39 | internalRequest.setLimit(limit);
40 | }
41 |
42 | /**
43 | * @param limit The maximum number of records to return.
44 | * @return A reference to this updated object so that method calls can be chained together.
45 | */
46 | @Override
47 | public GetRecordsRequest withLimit(Integer limit) {
48 | internalRequest.setLimit(limit);
49 | return this;
50 | }
51 |
52 | /**
53 | * @return The position in the shard from which you want to start sequentially
54 | * reading data records.
55 | */
56 | @Override
57 | public String getShardIterator() {
58 | return internalRequest.getShardIterator();
59 | }
60 |
61 | /**
62 | * @param shardIterator The position in the shard from which you want to start sequentially
63 | * reading data records.
64 | */
65 | @Override
66 | public void setShardIterator(String shardIterator) {
67 | internalRequest.setShardIterator(shardIterator);
68 | }
69 |
70 | /**
71 | * @param shardIterator The position in the shard from which you want to start sequentially
72 | * reading data records.
73 | * @return A reference to this updated object so that method calls can be chained together.
74 | */
75 | @Override
76 | public GetRecordsRequest withShardIterator(String shardIterator) {
77 | internalRequest.setShardIterator(shardIterator);
78 | return this;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetRecordsResultAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.kinesis.model.GetRecordsResult;
9 | import com.amazonaws.services.kinesis.model.Record;
10 |
11 | /**
12 | * Represents the output of a GetRecords operation.
13 | */
14 | public class GetRecordsResultAdapter extends GetRecordsResult {
15 |
16 | private com.amazonaws.services.dynamodbv2.model.GetRecordsResult internalResult;
17 |
18 | private java.util.List records;
19 |
20 | /**
21 | * Constructs a new result using a DynamoDBStreams object.
22 | *
23 | * @param result Instance of DynamoDBStreams GetRecordsResult
24 | */
25 | public GetRecordsResultAdapter(com.amazonaws.services.dynamodbv2.model.GetRecordsResult result) {
26 | this(result, true);
27 | }
28 |
29 | /**
30 | * Constructs a new result using a DynamoDBStreams object.
31 | *
32 | * @param result Instance of DynamoDBStreams GetRecordsResult
33 | * @param generateRecordDataBytes Whether or not RecordAdapters should generate the ByteBuffer returned by getData(). KCL
34 | * uses the bytes returned by getData to generate throughput metrics. If these metrics are not needed then
35 | * choosing to not generate this data results in memory and CPU savings.
36 | */
37 | public GetRecordsResultAdapter(com.amazonaws.services.dynamodbv2.model.GetRecordsResult result, boolean generateRecordDataBytes) {
38 | internalResult = result;
39 | records = new java.util.ArrayList();
40 | if (result.getRecords() != null) {
41 | for (com.amazonaws.services.dynamodbv2.model.Record record : result.getRecords()) {
42 | records.add(new RecordAdapter(record, generateRecordDataBytes));
43 | }
44 | }
45 | }
46 |
47 | /**
48 | * @return The data records retrieved from the shard
49 | */
50 | @Override
51 | public java.util.List getRecords() {
52 | return records;
53 | }
54 |
55 | @Override
56 | public void setRecords(java.util.Collection records) {
57 | throw new UnsupportedOperationException();
58 | }
59 |
60 | @Override
61 | public GetRecordsResult withRecords(Record... records) {
62 | throw new UnsupportedOperationException();
63 | }
64 |
65 | @Override
66 | public GetRecordsResult withRecords(java.util.Collection records) {
67 | throw new UnsupportedOperationException();
68 | }
69 |
70 | /**
71 | * @return The next position in the shard from which to start sequentially
72 | * reading data records. If set to null
, the shard has been
73 | * closed and the requested iterator will not return any more data.
74 | */
75 | @Override
76 | public String getNextShardIterator() {
77 | return internalResult.getNextShardIterator();
78 | }
79 |
80 | @Override
81 | public void setNextShardIterator(String nextShardIterator) {
82 | throw new UnsupportedOperationException();
83 | }
84 |
85 | @Override
86 | public GetRecordsResult withNextShardIterator(String nextShardIterator) {
87 | throw new UnsupportedOperationException();
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetShardIteratorRequestAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorRequest;
9 | import com.amazonaws.services.dynamodbv2.model.ShardIteratorType;
10 |
11 | /**
12 | * Container for the parameters to the GetShardIterator operation.
13 | */
14 | public class GetShardIteratorRequestAdapter extends GetShardIteratorRequest {
15 | // Evaluate each ShardIteratorType toString() only once.
16 | private static final String SHARD_ITERATOR_TYPE_DYNAMODB_AT_SEQUENCE_NUMBER = ShardIteratorType.AT_SEQUENCE_NUMBER.toString();
17 | private static final String SHARD_ITERATOR_TYPE_DYNAMODB_AFTER_SEQUENCE_NUMBER = ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString();
18 | private static final String SHARD_ITERATOR_TYPE_DYNAMODB_LATEST = ShardIteratorType.LATEST.toString();
19 | private static final String SHARD_ITERATOR_TYPE_DYNAMODB_TRIM_HORIZON = ShardIteratorType.TRIM_HORIZON.toString();
20 |
21 | private static final String SHARD_ITERATOR_TYPE_KINESIS_AFTER_SEQUENCE_NUMBER = com.amazonaws.services.kinesis.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString();
22 | private static final String SHARD_ITERATOR_TYPE_KINESIS_AT_SEQUENCE_NUMBER = com.amazonaws.services.kinesis.model.ShardIteratorType.AT_SEQUENCE_NUMBER.toString();
23 | private static final String SHARD_ITERATOR_TYPE_KINESIS_LATEST = com.amazonaws.services.kinesis.model.ShardIteratorType.LATEST.toString();
24 | private static final String SHARD_ITERATOR_TYPE_KINESIS_TRIM_HORIZON = com.amazonaws.services.kinesis.model.ShardIteratorType.TRIM_HORIZON.toString();
25 |
26 | private com.amazonaws.services.kinesis.model.GetShardIteratorRequest internalRequest;
27 |
28 | /**
29 | * Constructs a new request using an AmazonKinesis object.
30 | *
31 | * @param request Instance of AmazonKinesis GetShardIteratorRequest
32 | */
33 | public GetShardIteratorRequestAdapter(com.amazonaws.services.kinesis.model.GetShardIteratorRequest request) {
34 | internalRequest = request;
35 | }
36 |
37 | /**
38 | * @return The ARN of the stream.
39 | */
40 | @Override
41 | public String getStreamArn() {
42 | return internalRequest.getStreamName();
43 | }
44 |
45 | /**
46 | * @param streamArn The ARN of the stream.
47 | */
48 | @Override
49 | public void setStreamArn(String streamArn) {
50 | internalRequest.setStreamName(streamArn);
51 | }
52 |
53 | /**
54 | * @param streamArn The ARN of the stream.
55 | * @return Returns a reference to this object so that method calls can be chained together.
56 | */
57 | @Override
58 | public GetShardIteratorRequest withStreamArn(String streamArn) {
59 | internalRequest.withStreamName(streamArn);
60 | return this;
61 | }
62 |
63 | /**
64 | * @return The shard ID of the shard to get the iterator for.
65 | */
66 | @Override
67 | public String getShardId() {
68 | return internalRequest.getShardId();
69 | }
70 |
71 | /**
72 | * @param shardId The shard ID of the shard to get the iterator for.
73 | */
74 | @Override
75 | public void setShardId(String shardId) {
76 | internalRequest.setShardId(shardId);
77 | }
78 |
79 | /**
80 | * @param shardId The shard ID of the shard to get the iterator for.
81 | * @return Returns a reference to this object so that method calls can be chained together.
82 | */
83 | @Override
84 | public GetShardIteratorRequest withShardId(String shardId) {
85 | internalRequest.withShardId(shardId);
86 | return this;
87 | }
88 |
89 | /**
90 | * @return The sequence number of the data record in the shard from which to
91 | * start reading from.
92 | */
93 | @Override
94 | public String getSequenceNumber() {
95 | return internalRequest.getStartingSequenceNumber();
96 | }
97 |
98 | /**
99 | * @param sequenceNumber The sequence number of the data record in the shard from which to
100 | * start reading from.
101 | */
102 | @Override
103 | public void setSequenceNumber(String sequenceNumber) {
104 | internalRequest.setStartingSequenceNumber(sequenceNumber);
105 | }
106 |
107 | /**
108 | * @param sequenceNumber The sequence number of the data record in the shard from which to
109 | * start reading from.
110 | * @return Returns a reference to this object so that method calls can be chained together.
111 | */
112 | @Override
113 | public GetShardIteratorRequest withSequenceNumber(String sequenceNumber) {
114 | internalRequest.withStartingSequenceNumber(sequenceNumber);
115 | return this;
116 | }
117 |
118 | /**
119 | * @return Determines how the shard iterator is used to start reading data
120 | * records from the shard.
121 | */
122 | @Override
123 | public String getShardIteratorType() {
124 | return internalRequest.getShardIteratorType();
125 | }
126 |
127 | /**
128 | * @param shardIteratorType Determines how the shard iterator is used to start reading data
129 | * records from the shard.
130 | */
131 | @Override
132 | public void setShardIteratorType(String shardIteratorType) {
133 | if (SHARD_ITERATOR_TYPE_DYNAMODB_TRIM_HORIZON.equals(shardIteratorType)) {
134 | internalRequest.setShardIteratorType(SHARD_ITERATOR_TYPE_KINESIS_TRIM_HORIZON);
135 | } else if (SHARD_ITERATOR_TYPE_DYNAMODB_LATEST.equals(shardIteratorType)) {
136 | internalRequest.setShardIteratorType(SHARD_ITERATOR_TYPE_KINESIS_LATEST);
137 | } else if (SHARD_ITERATOR_TYPE_DYNAMODB_AT_SEQUENCE_NUMBER.equals(shardIteratorType)) {
138 | internalRequest.setShardIteratorType(SHARD_ITERATOR_TYPE_KINESIS_AT_SEQUENCE_NUMBER);
139 | } else if (SHARD_ITERATOR_TYPE_DYNAMODB_AFTER_SEQUENCE_NUMBER.equals(shardIteratorType)) {
140 | internalRequest.setShardIteratorType(SHARD_ITERATOR_TYPE_KINESIS_AFTER_SEQUENCE_NUMBER);
141 | } else {
142 | throw new IllegalArgumentException("Unsupported ShardIteratorType: " + shardIteratorType);
143 | }
144 | }
145 |
146 | /**
147 | * @param shardIteratorType Determines how the shard iterator is used to start reading data
148 | * records from the shard.
149 | */
150 | @Override
151 | public void setShardIteratorType(ShardIteratorType shardIteratorType) {
152 | setShardIteratorType(shardIteratorType.toString());
153 | }
154 |
155 | /**
156 | * @param shardIteratorType Determines how the shard iterator is used to start reading data
157 | * records from the shard.
158 | * @return Returns a reference to this object so that method calls can be chained together.
159 | */
160 | @Override
161 | public GetShardIteratorRequest withShardIteratorType(String shardIteratorType) {
162 | setShardIteratorType(shardIteratorType);
163 | return this;
164 | }
165 |
166 | /**
167 | * @param shardIteratorType Determines how the shard iterator is used to start reading data
168 | * records from the shard.
169 | * @return Returns a reference to this object so that method calls can be chained together.
170 | */
171 | @Override
172 | public GetShardIteratorRequest withShardIteratorType(ShardIteratorType shardIteratorType) {
173 | setShardIteratorType(shardIteratorType);
174 | return this;
175 | }
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetShardIteratorResultAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.kinesis.model.GetShardIteratorResult;
9 |
10 | /**
11 | * Represents the output of a GetShardIterator operation.
12 | */
13 | public class GetShardIteratorResultAdapter extends GetShardIteratorResult {
14 |
15 | private com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult internalResult;
16 |
17 | /**
18 | * Constructs a new result using a DynamoDBStreams object.
19 | *
20 | * @param result Instance of DynamoDBStreams GetShardIteratorResult
21 | */
22 | public GetShardIteratorResultAdapter(com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult result) {
23 | internalResult = result;
24 | }
25 |
26 | /**
27 | * @return The position in the shard from which to start reading data records
28 | * sequentially.
29 | */
30 | @Override
31 | public String getShardIterator() {
32 | return internalResult.getShardIterator();
33 | }
34 |
35 | @Override
36 | public void setShardIterator(String shardIterator) {
37 | throw new UnsupportedOperationException();
38 | }
39 |
40 | @Override
41 | public GetShardIteratorResult withShardIterator(String shardIterator) {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/ListStreamsRequestAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.dynamodbv2.model.ListStreamsRequest;
9 |
10 | /**
11 | * Container for the parameters to the ListStreams operation.
12 | */
13 | public class ListStreamsRequestAdapter extends ListStreamsRequest {
14 |
15 | private com.amazonaws.services.kinesis.model.ListStreamsRequest internalRequest;
16 |
17 | /**
18 | * Constructs a new request using an AmazonKinesis object.
19 | *
20 | * @param request Instance of AmazonKinesis ListStreamsRequest
21 | */
22 | public ListStreamsRequestAdapter(com.amazonaws.services.kinesis.model.ListStreamsRequest request) {
23 | internalRequest = request;
24 | }
25 |
26 | /**
27 | * @return The name of the stream to start the list with.
28 | */
29 | @Override
30 | public String getExclusiveStartStreamArn() {
31 | return internalRequest.getExclusiveStartStreamName();
32 | }
33 |
34 | /**
35 | * @param exclusiveStartStreamArn The name of the stream to start the list with.
36 | */
37 | @Override
38 | public void setExclusiveStartStreamArn(String exclusiveStartStreamArn) {
39 | internalRequest.setExclusiveStartStreamName(exclusiveStartStreamArn);
40 | }
41 |
42 | /**
43 | * @param exclusiveStartStreamArn The name of the stream to start the list with.
44 | * @return A reference to this updated object so that method calls can be chained together.
45 | */
46 | @Override
47 | public ListStreamsRequest withExclusiveStartStreamArn(String exclusiveStartStreamArn) {
48 | this.setExclusiveStartStreamArn(exclusiveStartStreamArn);
49 | return this;
50 | }
51 |
52 | /**
53 | * @return The maximum number of streams to list.
54 | */
55 | @Override
56 | public Integer getLimit() {
57 | return internalRequest.getLimit();
58 | }
59 |
60 | /**
61 | * @param limit The maximum number of streams to list.
62 | */
63 | @Override
64 | public void setLimit(Integer limit) {
65 | internalRequest.setLimit(limit);
66 | }
67 |
68 | /**
69 | * @param limit The maximum number of streams to list.
70 | * @return A reference to this updated object so that method calls can be chained together.
71 | */
72 | @Override
73 | public ListStreamsRequest withLimit(Integer limit) {
74 | this.setLimit(limit);
75 | return this;
76 | }
77 |
78 | // Not supported by the underlying Kinesis class
79 | @Override
80 | public String getTableName() {
81 | throw new UnsupportedOperationException();
82 | }
83 |
84 | // Not supported by the underlying Kinesis class
85 | @Override
86 | public void setTableName(String tableName) {
87 | throw new UnsupportedOperationException();
88 | }
89 |
90 | // Not supported by the underlying Kinesis class
91 | @Override
92 | public ListStreamsRequest withTableName(String tableName) {
93 | throw new UnsupportedOperationException();
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/ListStreamsResultAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import com.amazonaws.services.kinesis.model.ListStreamsResult;
12 |
13 | /**
14 | * Represents the output of a ListStreams operation.
15 | */
16 | public class ListStreamsResultAdapter extends ListStreamsResult {
17 |
18 | private com.amazonaws.services.dynamodbv2.model.ListStreamsResult internalResult;
19 |
20 | /**
21 | * Constructs a new result using a DynamoDBStreams object.
22 | *
23 | * @param result Instance of DynamoDBStreams ListStreamsResult
24 | */
25 | public ListStreamsResultAdapter(com.amazonaws.services.dynamodbv2.model.ListStreamsResult result) {
26 | internalResult = result;
27 | }
28 |
29 | /**
30 | * The names of the streams that are associated with the AWS account
31 | * making the request.
32 | */
33 | @Override
34 | public List getStreamNames() {
35 | List streams = internalResult.getStreams();
36 | List streamArns = new ArrayList<>(streams.size());
37 | for (com.amazonaws.services.dynamodbv2.model.Stream stream : streams) {
38 | streamArns.add(stream.getStreamArn());
39 | }
40 | return streamArns;
41 | }
42 |
43 | @Override
44 | public void setStreamNames(java.util.Collection streamNames) {
45 | throw new UnsupportedOperationException();
46 | }
47 |
48 | @Override
49 | public ListStreamsResult withStreamNames(java.util.Collection streamNames) {
50 | throw new UnsupportedOperationException();
51 | }
52 |
53 | @Override
54 | public ListStreamsResult withStreamNames(String... streamNames) {
55 | throw new UnsupportedOperationException();
56 | }
57 |
58 | /**
59 | * @return If true, there are more streams available to list.
60 | */
61 | @Override
62 | public Boolean isHasMoreStreams() {
63 | return internalResult.getLastEvaluatedStreamArn() != null;
64 | }
65 |
66 | /**
67 | * @return If true, there are more streams available to list.
68 | */
69 | @Override
70 | public Boolean getHasMoreStreams() {
71 | return internalResult.getLastEvaluatedStreamArn() != null;
72 | }
73 |
74 | @Override
75 | public void setHasMoreStreams(Boolean hasMoreStreams) {
76 | throw new UnsupportedOperationException();
77 | }
78 |
79 | @Override
80 | public ListStreamsResult withHasMoreStreams(Boolean hasMoreStreams) {
81 | throw new UnsupportedOperationException();
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/RecordAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import java.io.IOException;
9 | import java.nio.ByteBuffer;
10 | import java.nio.charset.Charset;
11 | import java.util.Date;
12 |
13 | import org.apache.commons.logging.Log;
14 | import org.apache.commons.logging.LogFactory;
15 |
16 | import com.amazonaws.services.kinesis.model.Record;
17 | import com.fasterxml.jackson.core.JsonProcessingException;
18 | import com.fasterxml.jackson.databind.ObjectMapper;
19 |
20 | /**
21 | * A single update notification of a DynamoDB Stream, adapted for use
22 | * with the Amazon Kinesis model.
23 | *
24 | * This class is designed to be used in a single thread only.
25 | */
26 | public class RecordAdapter extends Record {
27 |
28 | private static Log LOG = LogFactory.getLog(RecordAdapter.class);
29 |
30 | public static final Charset defaultCharset = Charset.forName("UTF-8");
31 |
32 | private static final ObjectMapper MAPPER = new RecordObjectMapper();
33 |
34 | private final com.amazonaws.services.dynamodbv2.model.Record internalRecord;
35 |
36 | private ByteBuffer data;
37 |
38 | private boolean generateDataBytes;
39 |
40 | /**
41 | * Constructs a new record using a DynamoDBStreams object.
42 | *
43 | * @param record Instance of DynamoDBStreams Record
44 | */
45 | public RecordAdapter(com.amazonaws.services.dynamodbv2.model.Record record) {
46 | this(record, true);
47 | }
48 |
49 | /**
50 | * Constructor for internal use
51 | *
52 | * @param record
53 | * @param generateDataBytes Whether or not to generate the ByteBuffer returned by getData(). KCL
54 | * uses the bytes returned by getData to generate throughput metrics. If these metrics are not needed then
55 | * choosing to not generate this data results in memory and CPU savings. If this value is true then
56 | * the data will be generated. If false, getData() will return an empty ByteBuffer.
57 | * @throws IOException
58 | */
59 | RecordAdapter(com.amazonaws.services.dynamodbv2.model.Record record, boolean generateDataBytes) {
60 | internalRecord = record;
61 | this.generateDataBytes = generateDataBytes;
62 | }
63 |
64 | /**
65 | * @return The underlying DynamoDBStreams object
66 | */
67 | public com.amazonaws.services.dynamodbv2.model.Record getInternalObject() {
68 | return internalRecord;
69 | }
70 |
71 | /**
72 | * @return The unique identifier for the record in the DynamoDB stream.
73 | */
74 | @Override
75 | public String getSequenceNumber() {
76 | return internalRecord.getDynamodb().getSequenceNumber();
77 | }
78 |
79 | @Override
80 | public void setSequenceNumber(String sequenceNumber) {
81 | throw new UnsupportedOperationException();
82 | }
83 |
84 | @Override
85 | public Record withSequenceNumber(String sequenceNumber) {
86 | throw new UnsupportedOperationException();
87 | }
88 |
89 | /**
90 | * This method returns JSON serialized {@link Record} object. However, This is not the best to use the object
91 | * It is recommended to get an object using {@link #getInternalObject()} and cast appropriately.
92 | *
93 | * @return JSON serialization of {@link Record} object. JSON contains only non-null
94 | * fields of {@link com.amazonaws.services.dynamodbv2.model.Record}. It returns null if serialization fails.
95 | */
96 | @Override
97 | public ByteBuffer getData() {
98 | if (data == null) {
99 | if (generateDataBytes) {
100 | try {
101 | data = ByteBuffer.wrap(MAPPER.writeValueAsString(internalRecord).getBytes(defaultCharset));
102 | } catch (JsonProcessingException e) {
103 | final String errorMessage = "Failed to serialize stream record to JSON";
104 | LOG.error(errorMessage, e);
105 | throw new RuntimeException(errorMessage, e);
106 | }
107 | } else {
108 | data = ByteBuffer.wrap(new byte[0]);
109 | }
110 | }
111 | return data;
112 | }
113 |
114 | @Override
115 | public void setData(ByteBuffer data) {
116 | throw new UnsupportedOperationException();
117 | }
118 |
119 | @Override
120 | public Record withData(ByteBuffer data) {
121 | throw new UnsupportedOperationException();
122 | }
123 |
124 | /**
125 | * Jackson ObjectMapper requires a valid return value for serialization.
126 | */
127 | @Override
128 | public String getPartitionKey() {
129 | return null;
130 | }
131 |
132 | @Override
133 | public void setPartitionKey(String partitionKey) {
134 | throw new UnsupportedOperationException();
135 | }
136 |
137 | @Override
138 | public Record withPartitionKey(String partitionKey) {
139 | throw new UnsupportedOperationException();
140 | }
141 |
142 | @Override
143 | public Date getApproximateArrivalTimestamp() {
144 | return internalRecord.getDynamodb().getApproximateCreationDateTime();
145 | }
146 |
147 | @Override
148 | public void setApproximateArrivalTimestamp(Date approximateArrivalTimestamp) {
149 | internalRecord.getDynamodb().setApproximateCreationDateTime(approximateArrivalTimestamp);
150 | }
151 |
152 | @Override
153 | public Record withApproximateArrivalTimestamp(Date approximateArrivalTimestamp) {
154 | setApproximateArrivalTimestamp(approximateArrivalTimestamp);
155 | return this;
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/ShardAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import com.amazonaws.services.kinesis.model.HashKeyRange;
9 | import com.amazonaws.services.kinesis.model.SequenceNumberRange;
10 | import com.amazonaws.services.kinesis.model.Shard;
11 |
12 | /**
13 | * A uniquely identified group of data records in a DynamoDB
14 | * stream.
15 | */
16 | public class ShardAdapter extends Shard {
17 |
18 | private com.amazonaws.services.dynamodbv2.model.Shard internalShard;
19 |
20 | /**
21 | * Constructs a new shard description using a DynamoDBStreams object.
22 | *
23 | * @param shard Instance of DynamoDBStreams Shard
24 | */
25 | public ShardAdapter(com.amazonaws.services.dynamodbv2.model.Shard shard) {
26 | internalShard = shard;
27 | }
28 |
29 | /**
30 | * @return The unique identifier of the shard within the DynamoDB stream.
31 | */
32 | @Override
33 | public String getShardId() {
34 | return internalShard.getShardId();
35 | }
36 |
37 | @Override
38 | public void setShardId(String shardId) {
39 | throw new UnsupportedOperationException();
40 | }
41 |
42 | @Override
43 | public Shard withShardId(String shardId) {
44 | throw new UnsupportedOperationException();
45 | }
46 |
47 | /**
48 | * @return The shard Id of the shard's parent.
49 | */
50 | @Override
51 | public String getParentShardId() {
52 | return internalShard.getParentShardId();
53 | }
54 |
55 | @Override
56 | public void setParentShardId(String parentShardId) {
57 | throw new UnsupportedOperationException();
58 | }
59 |
60 | @Override
61 | public Shard withParentShardId(String parentShardId) {
62 | throw new UnsupportedOperationException();
63 | }
64 |
65 | /**
66 | * The Kinesis model provides an adjacent parent shard ID in the event of
67 | * a parent shard merge. Since DynamoDB Streams does not support merge, this
68 | * always returns null.
69 | *
70 | * @return The shard Id of the shard adjacent to the shard's parent.
71 | */
72 | @Override
73 | public String getAdjacentParentShardId() {
74 | return null;
75 | }
76 |
77 | @Override
78 | public void setAdjacentParentShardId(String adjacentParentShardId) {
79 | throw new UnsupportedOperationException();
80 | }
81 |
82 | @Override
83 | public Shard withAdjacentParentShardId(String adjacentParentShardId) {
84 | throw new UnsupportedOperationException();
85 | }
86 |
87 | /**
88 | * The underlying DynamoDB Streams model does not expose hash key range. To
89 | * ensure compatibility with the Kinesis Client Library, this method
90 | * returns dummy values.
91 | *
92 | * @return The range of possible hash key values for the shard.
93 | */
94 | @Override
95 | public HashKeyRange getHashKeyRange() {
96 | HashKeyRange hashKeyRange = new HashKeyRange();
97 | hashKeyRange.setStartingHashKey(java.math.BigInteger.ZERO.toString());
98 | hashKeyRange.setEndingHashKey(java.math.BigInteger.ONE.toString());
99 | return hashKeyRange;
100 | }
101 |
102 | @Override
103 | public void setHashKeyRange(HashKeyRange hashKeyRange) {
104 | throw new UnsupportedOperationException();
105 | }
106 |
107 | @Override
108 | public Shard withHashKeyRange(HashKeyRange hashKeyRange) {
109 | throw new UnsupportedOperationException();
110 | }
111 |
112 | /**
113 | * @return The range of possible sequence numbers for the shard.
114 | */
115 | @Override
116 | public SequenceNumberRange getSequenceNumberRange() {
117 | SequenceNumberRange sequenceNumberRange = new SequenceNumberRange();
118 | sequenceNumberRange.setStartingSequenceNumber(internalShard.getSequenceNumberRange().getStartingSequenceNumber());
119 | sequenceNumberRange.setEndingSequenceNumber(internalShard.getSequenceNumberRange().getEndingSequenceNumber());
120 | return sequenceNumberRange;
121 | }
122 |
123 | @Override
124 | public void setSequenceNumberRange(SequenceNumberRange sequenceNumberRange) {
125 | throw new UnsupportedOperationException();
126 | }
127 |
128 | @Override
129 | public Shard withSequenceNumberRange(SequenceNumberRange sequenceNumberRange) {
130 | throw new UnsupportedOperationException();
131 | }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/StreamDescriptionAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import com.amazonaws.services.kinesis.model.Shard;
12 | import com.amazonaws.services.kinesis.model.StreamDescription;
13 | import com.amazonaws.services.kinesis.model.StreamStatus;
14 |
15 | /**
16 | * Container for all information describing a single DynamoDB Stream.
17 | */
18 | public class StreamDescriptionAdapter extends StreamDescription {
19 | // Evaluate each StreamStatus.toString() only once
20 | private static final String STREAM_STATUS_DYNAMODB_DISABLED = com.amazonaws.services.dynamodbv2.model.StreamStatus.DISABLED.toString();
21 | private static final String STREAM_STATUS_DYNAMODB_DISABLING = com.amazonaws.services.dynamodbv2.model.StreamStatus.DISABLING.toString();
22 | private static final String STREAM_STATUS_DYNAMODB_ENABLED = com.amazonaws.services.dynamodbv2.model.StreamStatus.ENABLED.toString();
23 | private static final String STREAM_STATUS_DYNAMODB_ENABLING = com.amazonaws.services.dynamodbv2.model.StreamStatus.ENABLING.toString();
24 | private static final String STREAM_STATUS_KINESIS_ACTIVE = StreamStatus.ACTIVE.toString();
25 | private static final String STREAM_STATUS_KINESIS_CREATING = StreamStatus.CREATING.toString();
26 |
27 | private final com.amazonaws.services.dynamodbv2.model.StreamDescription internalDescription;
28 |
29 | private final List shards;
30 |
31 | /**
32 | * Constructs a new description using a DynamoDBStreams object.
33 | *
34 | * @param streamDescription Instance of DynamoDBStreams StreamDescription
35 | */
36 | public StreamDescriptionAdapter(com.amazonaws.services.dynamodbv2.model.StreamDescription streamDescription) {
37 | internalDescription = streamDescription;
38 | shards = new ArrayList();
39 | for (com.amazonaws.services.dynamodbv2.model.Shard shard : streamDescription.getShards()) {
40 | shards.add(new ShardAdapter(shard));
41 | }
42 | }
43 |
44 | /**
45 | * @return The underlying DynamoDBStreams object
46 | */
47 | public com.amazonaws.services.dynamodbv2.model.StreamDescription getInternalObject() {
48 | return internalDescription;
49 | }
50 |
51 | /**
52 | * @return The name of the stream being described.
53 | */
54 | @Override
55 | public String getStreamName() {
56 | return internalDescription.getStreamArn();
57 | }
58 |
59 | @Override
60 | public void setStreamName(String streamName) {
61 | throw new UnsupportedOperationException();
62 | }
63 |
64 | @Override
65 | public StreamDescription withStreamName(String streamName) {
66 | throw new UnsupportedOperationException();
67 | }
68 |
69 | /**
70 | * @return The Amazon Resource Name (ARN) for the stream being described.
71 | */
72 | @Override
73 | public String getStreamARN() {
74 | return internalDescription.getStreamArn();
75 | }
76 |
77 | @Override
78 | public void setStreamARN(String streamARN) {
79 | throw new UnsupportedOperationException();
80 | }
81 |
82 | @Override
83 | public StreamDescription withStreamARN(String streamARN) {
84 | throw new UnsupportedOperationException();
85 | }
86 |
87 | /**
88 | * @return The current status of the stream being described.
89 | */
90 | @Override
91 | public String getStreamStatus() {
92 | String status = internalDescription.getStreamStatus();
93 | if (STREAM_STATUS_DYNAMODB_ENABLED.equals(status)) {
94 | status = STREAM_STATUS_KINESIS_ACTIVE;
95 | } else if (STREAM_STATUS_DYNAMODB_ENABLING.equals(status)) {
96 | status = STREAM_STATUS_KINESIS_CREATING;
97 | } else if (STREAM_STATUS_DYNAMODB_DISABLED.equals(status)) {
98 | // streams are valid for 24hrs after disabling and
99 | // will continue to support read operations
100 | status = STREAM_STATUS_KINESIS_ACTIVE;
101 | } else if (STREAM_STATUS_DYNAMODB_DISABLING.equals(status)) {
102 | status = STREAM_STATUS_KINESIS_ACTIVE;
103 | } else {
104 | throw new UnsupportedOperationException("Unsupported StreamStatus: " + status);
105 | }
106 | return status;
107 | }
108 |
109 | @Override
110 | public void setStreamStatus(String streamStatus) {
111 | throw new UnsupportedOperationException();
112 | }
113 |
114 | @Override
115 | public StreamDescription withStreamStatus(String streamStatus) {
116 | throw new UnsupportedOperationException();
117 | }
118 |
119 | @Override
120 | public void setStreamStatus(StreamStatus streamStatus) {
121 | throw new UnsupportedOperationException();
122 | }
123 |
124 | @Override
125 | public StreamDescription withStreamStatus(StreamStatus streamStatus) {
126 | throw new UnsupportedOperationException();
127 | }
128 |
129 | /**
130 | * @return The shards that comprise the stream.
131 | */
132 | @Override
133 | public List getShards() {
134 | return shards;
135 | }
136 |
137 | @Override
138 | public void setShards(java.util.Collection shards) {
139 | throw new UnsupportedOperationException();
140 | }
141 |
142 | @Override
143 | public StreamDescription withShards(Shard... shards) {
144 | throw new UnsupportedOperationException();
145 | }
146 |
147 | @Override
148 | public StreamDescription withShards(java.util.Collection shards) {
149 | throw new UnsupportedOperationException();
150 | }
151 |
152 | /**
153 | * @return If true there are more shards in the stream
154 | * available to describe.
155 | */
156 | @Override
157 | public Boolean isHasMoreShards() {
158 | return internalDescription.getLastEvaluatedShardId() != null;
159 | }
160 |
161 | /**
162 | * @return If true there are more shards in the stream
163 | * available to describe.
164 | */
165 | @Override
166 | public Boolean getHasMoreShards() {
167 | return internalDescription.getLastEvaluatedShardId() != null;
168 | }
169 |
170 | @Override
171 | public void setHasMoreShards(Boolean hasMoreShards) {
172 | throw new UnsupportedOperationException();
173 | }
174 |
175 | @Override
176 | public StreamDescription withHasMoreShards(Boolean hasMoreShards) {
177 | throw new UnsupportedOperationException();
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/utils/Sleeper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Amazon Software License (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/asl/
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | package com.amazonaws.services.dynamodbv2.streamsadapter.utils;
16 |
17 | /**
18 | * Interface to implement mechanisms to inject specified delay
19 | * in processing similar to Thread.sleep(long millis).
20 | */
21 | public interface Sleeper {
22 |
23 | void sleep(long intervalInMillis);
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/amazonaws/services/dynamodbv2/streamsadapter/utils/ThreadSleeper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Amazon Software License (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/asl/
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 | package com.amazonaws.services.dynamodbv2.streamsadapter.utils;
16 |
17 | import org.apache.commons.logging.Log;
18 | import org.apache.commons.logging.LogFactory;
19 |
20 | /**
21 | * Simple abstraction over Thread.sleep() to allow unit testing of backoff mechanisms.
22 | */
23 | public class ThreadSleeper implements Sleeper {
24 | private static final Log LOG = LogFactory.getLog(ThreadSleeper.class);
25 |
26 | @Override public void sleep(long intervalInMillis) {
27 | try {
28 | Thread.sleep(intervalInMillis);
29 | } catch (InterruptedException ie) {
30 | LOG.debug("ThreadSleeper sleep was interrupted ", ie);
31 | Thread.currentThread().interrupt();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/AdapterRequestCacheTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertNull;
10 | import static org.junit.Assert.assertTrue;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.Random;
15 |
16 | import org.junit.Test;
17 |
18 | import com.amazonaws.AmazonWebServiceRequest;
19 | import com.amazonaws.services.dynamodbv2.streamsadapter.model.GetRecordsRequestAdapter;
20 | import com.amazonaws.services.kinesis.model.GetRecordsRequest;
21 |
22 | public class AdapterRequestCacheTests {
23 |
24 | private static final int CACHE_SIZE = 50;
25 |
26 | @Test
27 | public void testSanityConstructor() {
28 | AdapterRequestCache requestCache = new AdapterRequestCache(CACHE_SIZE);
29 | assertTrue(requestCache instanceof AdapterRequestCache);
30 | }
31 |
32 | @Test(expected = IllegalArgumentException.class)
33 | public void testZeroCapacity() {
34 | new AdapterRequestCache(0);
35 | }
36 |
37 | @Test(expected = IllegalArgumentException.class)
38 | public void testNegativeCapacity() {
39 | Random r = new Random();
40 | int positiveNumber = r.nextInt(Integer.MAX_VALUE - 1) + 1;
41 | new AdapterRequestCache(-1 * positiveNumber);
42 | }
43 |
44 | @Test(expected = IllegalArgumentException.class)
45 | public void testNullRequestAdd() {
46 | AdapterRequestCache requestCache = new AdapterRequestCache(CACHE_SIZE);
47 | requestCache.addEntry(null, new GetRecordsRequestAdapter(new GetRecordsRequest()));
48 | }
49 |
50 | @Test(expected = IllegalArgumentException.class)
51 | public void testNullRequestAdapterAdd() {
52 | AdapterRequestCache requestCache = new AdapterRequestCache(CACHE_SIZE);
53 | requestCache.addEntry(new GetRecordsRequest(), null);
54 | }
55 |
56 | @Test(expected = IllegalArgumentException.class)
57 | public void testNullRequestGet() {
58 | AdapterRequestCache requestCache = new AdapterRequestCache(CACHE_SIZE);
59 | requestCache.getEntry(null);
60 | }
61 |
62 | @Test
63 | public void testCacheSanity() {
64 | AdapterRequestCache requestCache = new AdapterRequestCache(CACHE_SIZE);
65 | GetRecordsRequest request = new GetRecordsRequest();
66 | GetRecordsRequestAdapter requestAdapter = new GetRecordsRequestAdapter(request);
67 | requestCache.addEntry(request, requestAdapter);
68 | AmazonWebServiceRequest entry = requestCache.getEntry(request);
69 | assertEquals(System.identityHashCode(requestAdapter), System.identityHashCode(entry));
70 | }
71 |
72 | @Test
73 | public void testEviction() {
74 | AdapterRequestCache requestCache = new AdapterRequestCache(CACHE_SIZE);
75 | int testLength = 2 * CACHE_SIZE;
76 | List requests = new ArrayList(testLength);
77 | List requestAdapters = new ArrayList(testLength);
78 | for (int i = 0; i < testLength; i++) {
79 | // Construct requests
80 | GetRecordsRequest request = new GetRecordsRequest();
81 | GetRecordsRequestAdapter requestAdapter = new GetRecordsRequestAdapter(request);
82 | // Store references to request for validation
83 | requests.add(request);
84 | requestAdapters.add(requestAdapter);
85 | // Add entry to the request cache
86 | requestCache.addEntry(request, requestAdapter);
87 |
88 | // Verify request cache
89 | for (int j = 0; j <= i; j++) {
90 | AmazonWebServiceRequest expected = requestAdapters.get(j);
91 | AmazonWebServiceRequest actual = requestCache.getEntry(requests.get(j));
92 | if (j <= i - CACHE_SIZE) {
93 | assertNull(actual);
94 | } else {
95 | assertEquals(System.identityHashCode(expected), System.identityHashCode(actual));
96 | }
97 | }
98 |
99 | }
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/DynamoDBStreamsPeriodicShardSyncManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.amazonaws.services.dynamodbv2.streamsadapter;
2 |
3 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.IPeriodicShardSyncManager;
4 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.LeaderDecider;
5 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShardSyncTask;
6 | import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
7 | import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
8 | import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
9 | import com.amazonaws.services.kinesis.metrics.impl.NullMetricsFactory;
10 | import com.amazonaws.services.kinesis.metrics.interfaces.IMetricsFactory;
11 | import org.junit.Assert;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.mockito.Mock;
16 | import org.mockito.runners.MockitoJUnitRunner;
17 |
18 | import java.util.Collections;
19 | import static org.mockito.Mockito.when;
20 |
21 | @RunWith(MockitoJUnitRunner.class)
22 | public class DynamoDBStreamsPeriodicShardSyncManagerTest {
23 | private static final String WORKER_ID = "workerId";
24 | public static final long LEASES_RECOVERY_AUDITOR_EXECUTION_FREQUENCY_MILLIS = 2 * 60 * 1000L;
25 | public static final int LEASES_RECOVERY_AUDITOR_INCONSISTENCY_CONFIDENCE_THRESHOLD = 3;
26 |
27 | /** Manager for PERIODIC shard sync strategy */
28 | private IPeriodicShardSyncManager periodicShardSyncManager;
29 |
30 | /** Manager for SHARD_END shard sync strategy */
31 | private IPeriodicShardSyncManager auditorPeriodicShardSyncManager;
32 |
33 | @Mock
34 | private LeaderDecider leaderDecider;
35 | @Mock
36 | private ShardSyncTask shardSyncTask;
37 | @Mock
38 | private ILeaseManager leaseManager;
39 | @Mock
40 | private IKinesisProxy kinesisProxy;
41 |
42 | private IMetricsFactory metricsFactory = new NullMetricsFactory();
43 |
44 | @Before
45 | public void setup() {
46 | periodicShardSyncManager = new DynamoDBStreamsPeriodicShardSyncManager(WORKER_ID, leaderDecider, shardSyncTask,
47 | metricsFactory, leaseManager, kinesisProxy, false, LEASES_RECOVERY_AUDITOR_EXECUTION_FREQUENCY_MILLIS,
48 | LEASES_RECOVERY_AUDITOR_INCONSISTENCY_CONFIDENCE_THRESHOLD);
49 | auditorPeriodicShardSyncManager = new DynamoDBStreamsPeriodicShardSyncManager(WORKER_ID, leaderDecider, shardSyncTask,
50 | metricsFactory, leaseManager, kinesisProxy, true, LEASES_RECOVERY_AUDITOR_EXECUTION_FREQUENCY_MILLIS,
51 | LEASES_RECOVERY_AUDITOR_INCONSISTENCY_CONFIDENCE_THRESHOLD);
52 | }
53 |
54 | @Test
55 | public void testIfShardSyncIsInitiatedWhenNoLeasesArePassed() throws Exception {
56 | when(leaseManager.listLeases()).thenReturn(null);
57 | Assert.assertTrue(((DynamoDBStreamsPeriodicShardSyncManager) periodicShardSyncManager).checkForShardSync().shouldDoShardSync());
58 | Assert.assertTrue(((DynamoDBStreamsPeriodicShardSyncManager) auditorPeriodicShardSyncManager).checkForShardSync().shouldDoShardSync());
59 | }
60 |
61 | @Test
62 | public void testIfShardSyncIsInitiatedWhenEmptyLeasesArePassed() throws Exception {
63 | when(leaseManager.listLeases()).thenReturn(Collections.emptyList());
64 | Assert.assertTrue(((DynamoDBStreamsPeriodicShardSyncManager) periodicShardSyncManager).checkForShardSync().shouldDoShardSync());
65 | Assert.assertTrue(((DynamoDBStreamsPeriodicShardSyncManager) auditorPeriodicShardSyncManager).checkForShardSync().shouldDoShardSync());
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/PeriodicShardSyncTestBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.Random;
11 |
12 | import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
13 | import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
14 |
15 | public class PeriodicShardSyncTestBase {
16 |
17 | private static final String LEASE_KEY = "lease_key";
18 | private static final String LEASE_OWNER = "lease_owner";
19 |
20 | protected List getLeases(int count, boolean duplicateLeaseOwner, boolean activeLeases) {
21 | List leases = new ArrayList<>();
22 | for (int i=0;i leaseManager;
42 |
43 | @Mock
44 | private ScheduledExecutorService scheduledExecutorService;
45 |
46 | private int numShardSyncWorkers;
47 |
48 | @Before
49 | public void setup() {
50 | numShardSyncWorkers = PERIODIC_SHARD_SYNC_MAX_WORKERS_DEFAULT;
51 | leaderDecider = new StreamsDeterministicShuffleShardSyncLeaderDecider(config, leaseManager, scheduledExecutorService, numShardSyncWorkers);
52 | config = new KinesisClientLibConfiguration("Test", null, null, null);
53 | }
54 |
55 | @Test
56 | public void testLeaderElectionWithNullLeases() {
57 | boolean isLeader = leaderDecider.isLeader(WORKER_ID);
58 | assertTrue("IsLeader should return true if leaders is null", isLeader);
59 | }
60 |
61 | @Test
62 | public void testLeaderElectionWithEmptyLeases() throws Exception{
63 | when(leaseManager.listLeases()).thenReturn(new ArrayList<>());
64 | boolean isLeader = leaderDecider.isLeader(WORKER_ID);
65 | assertTrue("IsLeader should return true if no leases are returned", isLeader);
66 | }
67 |
68 | @Test
69 | public void testElectedLeadersAsPerExpectedShufflingOrder() throws Exception {
70 | List leases = getLeases(5, false /* duplicateLeaseOwner */, true /* activeLeases */);
71 | when(leaseManager.listLeases()).thenReturn(leases);
72 | Set expectedLeaders = getExpectedLeaders(leases);
73 | for (String leader : expectedLeaders) {
74 | assertTrue(leaderDecider.isLeader(leader));
75 | }
76 | for (KinesisClientLease lease : leases) {
77 | if (!expectedLeaders.contains(lease.getLeaseOwner())) {
78 | assertFalse(leaderDecider.isLeader(lease.getLeaseOwner()));
79 | }
80 | }
81 | }
82 |
83 | @Test
84 | public void testElectedLeadersAsPerExpectedShufflingOrderWhenUniqueWorkersLessThanMaxLeaders() {
85 | this.numShardSyncWorkers = 5; // More than number of unique lease owners
86 | leaderDecider = new StreamsDeterministicShuffleShardSyncLeaderDecider(config, leaseManager, scheduledExecutorService, numShardSyncWorkers);
87 | List leases = getLeases(3, false /* duplicateLeaseOwner */, true /* activeLeases */);
88 | Set expectedLeaders = getExpectedLeaders(leases);
89 | // All lease owners should be present in expected leaders set, and they should all be leaders.
90 | for (KinesisClientLease lease : leases) {
91 | assertTrue(leaderDecider.isLeader(lease.getLeaseOwner()));
92 | assertTrue(expectedLeaders.contains(lease.getLeaseOwner()));
93 | }
94 | }
95 |
96 | private Set getExpectedLeaders(List leases) {
97 | List uniqueHosts = leases.stream().filter(lease -> lease.getLeaseOwner() != null)
98 | .map(KinesisClientLease::getLeaseOwner).distinct().sorted().collect(Collectors.toList());
99 |
100 | Collections.shuffle(uniqueHosts, new Random(DETERMINISTIC_SHUFFLE_SEED));
101 | int numWorkers = Math.min(uniqueHosts.size(), this.numShardSyncWorkers);
102 | return new HashSet<>(uniqueHosts.subList(0, numWorkers));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/StreamsRecordProcessorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | import java.io.IOException;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import org.junit.Before;
15 | import org.junit.Test;
16 |
17 | import com.amazonaws.services.dynamodbv2.model.Record;
18 | import com.amazonaws.services.dynamodbv2.model.StreamRecord;
19 | import com.amazonaws.services.dynamodbv2.streamsadapter.model.RecordAdapter;
20 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorCheckpointer;
21 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
22 | import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
23 | import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
24 | import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
25 |
26 | public class StreamsRecordProcessorTest {
27 |
28 | private Record testRecord;
29 | private IRecordProcessor recordProcessor;
30 |
31 | @Before
32 | public void setUp() {
33 | recordProcessor = new SimpleStreamsRecordProcessor();
34 | }
35 |
36 | @Test
37 | public void testProcessRecordsSuccess() throws IOException {
38 | testRecord = new Record().withDynamodb(new StreamRecord()).withEventID("test").withEventName("MODIFY");
39 | RecordAdapter adapter = new RecordAdapter(testRecord);
40 | List recordList = new ArrayList();
41 | recordList.add(adapter);
42 | recordProcessor.processRecords(new ProcessRecordsInput().withRecords(recordList));
43 | }
44 |
45 | @Test(expected = IllegalArgumentException.class)
46 | public void testProcessRecordsFail() {
47 | List recordList = new ArrayList();
48 | recordList.add(new com.amazonaws.services.kinesis.model.Record());
49 | recordProcessor.processRecords(new ProcessRecordsInput().withRecords(recordList));
50 | }
51 |
52 | private class SimpleStreamsRecordProcessor extends StreamsRecordProcessor {
53 |
54 | @Override
55 | public void initialize(InitializationInput initializationInput) {
56 |
57 | }
58 |
59 | @Override
60 | public void processStreamsRecords(List records, IRecordProcessorCheckpointer checkpointer) {
61 | assertEquals(testRecord, records.get(0));
62 | }
63 |
64 | @Override
65 | public void shutdown(ShutdownInput shutdownInput) {
66 |
67 | }
68 |
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/functionals/CorrectnessTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.functionals;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | import java.util.concurrent.Executors;
11 | import java.util.concurrent.ScheduledExecutorService;
12 | import java.util.concurrent.TimeUnit;
13 |
14 | import org.apache.commons.logging.Log;
15 | import org.apache.commons.logging.LogFactory;
16 | import org.junit.Before;
17 | import org.junit.Test;
18 |
19 | import com.amazonaws.services.dynamodbv2.model.QueryResult;
20 | import com.amazonaws.services.dynamodbv2.model.ScanResult;
21 | import com.amazonaws.services.dynamodbv2.streamsadapter.util.ReplicatingRecordProcessor;
22 | import com.amazonaws.services.dynamodbv2.streamsadapter.util.TestUtil;
23 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.InitialPositionInStream;
24 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;
25 |
26 | /**
27 | * This test runs KCL with the DynamoDB Streams Kinesis Adapter using a single partition table and no shard lineage in
28 | * an embedded DynamoDB local instance. A series of operations are performed on the source table and then replicated on
29 | * a destination table using the records received by the IRecordProcessor implementation. Finally, a scan/query is
30 | * performed on both tables to assert that the expected records are replicated.
31 | */
32 | public class CorrectnessTest extends FunctionalTestBase {
33 | private static final Log LOG = LogFactory.getLog(CorrectnessTest.class);
34 |
35 | private int numItemsInSrcTable = 0;
36 |
37 | private static int NUM_INITIAL_ITEMS = 2;
38 |
39 | @Before
40 | public void setup() {
41 | super.setup();
42 | insertAndUpdateItems(NUM_INITIAL_ITEMS);
43 | }
44 |
45 | @Test
46 | public void trimHorizonTest() throws Exception {
47 | LOG.info("Starting single shard KCL integration test with TRIM_HORIZON.");
48 |
49 | KinesisClientLibConfiguration workerConfig =
50 | new KinesisClientLibConfiguration(leaseTable, streamId, credentials, KCL_WORKER_ID).withInitialPositionInStream(InitialPositionInStream.TRIM_HORIZON);
51 |
52 | startKCLWorker(workerConfig);
53 |
54 | while (recordProcessorFactory.getNumRecordsProcessed() < (2 * numItemsInSrcTable) /* Num of expected stream records */) {
55 | LOG.info("Sleep till all records are processed");
56 | Thread.sleep(THREAD_SLEEP_2S);
57 | }
58 |
59 | shutDownKCLWorker();
60 |
61 | ScanResult srcTableScan = TestUtil.scanTable(dynamoDBClient, srcTable);
62 | ScanResult destTableScan = TestUtil.scanTable(dynamoDBClient, destTable);
63 | assertEquals(srcTableScan.getItems(), destTableScan.getItems());
64 | }
65 |
66 | @Test
67 | public void latestTest() throws Exception {
68 | LOG.info("Starting single shard KCL integration test with LATEST.");
69 |
70 | KinesisClientLibConfiguration workerConfig =
71 | new KinesisClientLibConfiguration(leaseTable, streamId, credentials, KCL_WORKER_ID).withInitialPositionInStream(InitialPositionInStream.LATEST);
72 |
73 | startKCLWorker(workerConfig);
74 |
75 | while (recordProcessorFactory.getNumRecordsProcessed() < 0) {
76 | LOG.info("Sleep till RecordProcessor is initialized");
77 | Thread.sleep(THREAD_SLEEP_2S);
78 | }
79 |
80 | /* Only the following records will be processed by KCL since it is reading only the latest stream entries */
81 | int numNewItemsToInsert = 1;
82 | insertAndUpdateItems(numNewItemsToInsert);
83 |
84 | while (recordProcessorFactory.getNumRecordsProcessed() < 2 * numNewItemsToInsert) {
85 | LOG.info("Sleep till all records are processed");
86 | Thread.sleep(THREAD_SLEEP_2S);
87 | }
88 |
89 | shutDownKCLWorker();
90 |
91 | String lastInsertedPartitionKey = Integer.toString(100 + this.numItemsInSrcTable);
92 | QueryResult srcTableQuery = TestUtil.queryTable(dynamoDBClient, srcTable, lastInsertedPartitionKey);
93 | ScanResult destTableScan = TestUtil.scanTable(dynamoDBClient, destTable);
94 | assertEquals(srcTableQuery.getItems(), destTableScan.getItems());
95 | }
96 |
97 | /**
98 | * This test spawns a thread to periodically write items to the source table. It shuts down and restarts the KCL
99 | * worker while writes are happening (to simulate the real-world situation of a worker dying and another taking its
100 | * place). There are two things being verified here:
101 | * 1. New KCL worker resumes from the checkpoint
102 | * 2. All stream records are processed
103 | *
104 | * @throws Exception
105 | */
106 | @Test
107 | public void workerFailureTest() throws Exception {
108 | LOG.info("Starting single shard KCL worker failure test.");
109 |
110 | KinesisClientLibConfiguration workerConfig =
111 | new KinesisClientLibConfiguration(leaseTable, streamId, credentials, KCL_WORKER_ID).withInitialPositionInStream(InitialPositionInStream.TRIM_HORIZON);
112 |
113 | startKCLWorker(workerConfig);
114 |
115 | // A thread that keeps writing to the table every 2 seconds
116 | ScheduledExecutorService loadGeneratorService = Executors.newSingleThreadScheduledExecutor();
117 | loadGeneratorService.scheduleAtFixedRate(new Runnable() {
118 |
119 | @Override
120 | public void run() {
121 | insertAndUpdateItems(1);
122 | }
123 | }, 0/* initialDelay */, 2/* period */, TimeUnit.SECONDS);
124 |
125 | while (recordProcessorFactory.getNumRecordsProcessed() < 10) {
126 | LOG.info("Sleep till first few records are processed");
127 | Thread.sleep(THREAD_SLEEP_2S);
128 | }
129 |
130 | shutDownKCLWorker();
131 |
132 | // Calculate number of records processed by first worker and also the number of processed-but-not-checkpointed
133 | // records, since checkpoint happens after every batch of 10 records
134 | int numRecordsProcessedByFirstWorker = recordProcessorFactory.getNumRecordsProcessed();
135 | int numRecordsNotCheckpointed = numRecordsProcessedByFirstWorker % ReplicatingRecordProcessor.CHECKPOINT_BATCH_SIZE;
136 |
137 | // Start a new worker
138 | startKCLWorker(workerConfig);
139 |
140 | while (recordProcessorFactory.getNumRecordsProcessed() < 0) {
141 | LOG.info("Sleep till RecordProcessor is initialized");
142 | Thread.sleep(THREAD_SLEEP_2S);
143 | }
144 |
145 | loadGeneratorService.shutdown();
146 |
147 | if (!loadGeneratorService.awaitTermination(THREAD_SLEEP_5S, TimeUnit.MILLISECONDS)) {
148 | loadGeneratorService.shutdownNow();
149 | }
150 |
151 | int numStreamRecords = 2 * this.numItemsInSrcTable;
152 | int remainingRecordsToBeProcessed = numStreamRecords - numRecordsProcessedByFirstWorker + numRecordsNotCheckpointed;
153 |
154 | /*
155 | * The second worker must process atleast remainingRecordsToBeProcessed
156 | * num of records so that we have replicated everything to destination
157 | * table. Thus, this should never technically end up as an infinite
158 | * loop. If it does, something else is gone wrong.
159 | */
160 | while (recordProcessorFactory.getNumRecordsProcessed() < remainingRecordsToBeProcessed) {
161 | LOG.info("Sleep till remaining records are processed");
162 | Thread.sleep(THREAD_SLEEP_2S);
163 | }
164 |
165 | shutDownKCLWorker();
166 |
167 | ScanResult srcTableScan = TestUtil.scanTable(dynamoDBClient, srcTable);
168 | ScanResult destTableScan = TestUtil.scanTable(dynamoDBClient, destTable);
169 | assertEquals(srcTableScan.getItems(), destTableScan.getItems());
170 | }
171 |
172 | /**
173 | * This method will insert items sequentially with hash keys 101, 102 and so on. The updateItem call adds a new
174 | * attribute to the previously inserted item
175 | *
176 | * @param numItemsToInsert
177 | */
178 | private void insertAndUpdateItems(int numItemsToInsert) {
179 | for (int i = 1; i <= numItemsToInsert; i++) {
180 | numItemsInSrcTable++;
181 | String partitionKey = Integer.toString(100 + numItemsInSrcTable);
182 | String attribute1 = partitionKey + "-attr1";
183 | String attribute2 = partitionKey + "-attr2";
184 |
185 | TestUtil.putItem(dynamoDBClient, srcTable, partitionKey, attribute1);
186 | TestUtil.updateItem(dynamoDBClient, srcTable, partitionKey, attribute2);
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/functionals/FunctionalTestBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.functionals;
7 |
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | import org.apache.commons.logging.Log;
13 | import org.apache.commons.logging.LogFactory;
14 | import org.junit.After;
15 | import org.junit.Before;
16 |
17 | import com.amazonaws.auth.AWSCredentialsProvider;
18 | import com.amazonaws.auth.BasicAWSCredentials;
19 | import com.amazonaws.internal.StaticCredentialsProvider;
20 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
21 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreams;
22 | import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
23 | import com.amazonaws.services.dynamodbv2.local.shared.access.AmazonDynamoDBLocal;
24 | import com.amazonaws.services.dynamodbv2.model.DeleteTableRequest;
25 | import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient;
26 | import com.amazonaws.services.dynamodbv2.streamsadapter.StreamsWorkerFactory;
27 | import com.amazonaws.services.dynamodbv2.streamsadapter.util.TestRecordProcessorFactory;
28 | import com.amazonaws.services.dynamodbv2.streamsadapter.util.TestUtil;
29 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;
30 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.Worker;
31 | import com.amazonaws.services.kinesis.metrics.impl.NullMetricsFactory;
32 |
33 | /**
34 | * This base class sets up DynamoDB, Kinesis Adapter and DynamoDB streams clients used by a KCL worker operating on DynamoDB
35 | * Streams. It also creates required DynamoDB tables.
36 | */
37 | public abstract class FunctionalTestBase {
38 | private static final Log LOG = LogFactory.getLog(FunctionalTestBase.class);
39 |
40 | protected AmazonDynamoDBLocal dynamoDBLocal;
41 | protected AmazonDynamoDBStreams streamsClient;
42 | protected AmazonDynamoDBStreamsAdapterClient adapterClient;
43 | protected AmazonDynamoDB dynamoDBClient;
44 |
45 | protected AWSCredentialsProvider credentials;
46 | protected String streamId;
47 |
48 | protected Worker worker;
49 | protected TestRecordProcessorFactory recordProcessorFactory;
50 | protected ExecutorService workerThread;
51 |
52 | private static String accessKeyId = "KCLIntegTest";
53 | private static String secretAccessKey = "dummy";
54 |
55 | protected static String serviceName = "dynamodb";
56 | protected static String dynamodbEndpoint = "dummyEndpoint";
57 |
58 | protected static String srcTable = "kcl-integ-test-src";
59 | protected static String destTable = "kcl-integ-test-dest";
60 | protected static String leaseTable = "kcl-integ-test-leases";
61 |
62 | protected static int THREAD_SLEEP_5S = 5000;
63 | protected static int THREAD_SLEEP_2S = 2000;
64 | protected static String KCL_WORKER_ID = "kcl-integration-test-worker";
65 |
66 | @Before
67 | public void setup() {
68 | credentials = new StaticCredentialsProvider(new BasicAWSCredentials(accessKeyId, secretAccessKey));
69 |
70 | dynamoDBLocal = DynamoDBEmbedded.create();
71 | dynamoDBClient = dynamoDBLocal.amazonDynamoDB();
72 | streamsClient = dynamoDBLocal.amazonDynamoDBStreams();
73 |
74 | adapterClient = new AmazonDynamoDBStreamsAdapterClient(streamsClient);
75 |
76 | streamId = TestUtil.createTable(dynamoDBClient, srcTable, true /*With streams enabled*/);
77 | TestUtil.createTable(dynamoDBClient, destTable, false /* No streams */);
78 |
79 | TestUtil.waitForTableActive(dynamoDBClient, srcTable);
80 | TestUtil.waitForTableActive(dynamoDBClient, destTable);
81 | }
82 |
83 | @After
84 | public void teardown() {
85 | dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(srcTable));
86 | dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(destTable));
87 | dynamoDBClient.deleteTable(new DeleteTableRequest().withTableName(leaseTable));
88 |
89 | dynamoDBLocal.shutdown();
90 | }
91 |
92 | protected void startKCLWorker(KinesisClientLibConfiguration workerConfig) {
93 |
94 | recordProcessorFactory = new TestRecordProcessorFactory(dynamoDBClient, destTable);
95 |
96 | LOG.info("Creating worker for stream: " + streamId);
97 | worker = StreamsWorkerFactory
98 | .createDynamoDbStreamsWorker(recordProcessorFactory, workerConfig, adapterClient, dynamoDBClient, new NullMetricsFactory(), Executors.newCachedThreadPool());
99 |
100 | LOG.info("Starting worker...");
101 | workerThread = Executors.newSingleThreadExecutor();
102 | workerThread.submit(worker);
103 |
104 | workerThread.shutdown(); //This will wait till the KCL worker exits
105 | }
106 |
107 | protected void shutDownKCLWorker() throws Exception {
108 | worker.shutdown();
109 |
110 | if (!workerThread.awaitTermination(THREAD_SLEEP_5S, TimeUnit.MILLISECONDS)) {
111 | workerThread.shutdownNow();
112 | }
113 |
114 | LOG.info("Processing complete.");
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/functionals/KinesisParametersTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.functionals;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertTrue;
10 |
11 | import com.amazonaws.services.dynamodbv2.model.BillingMode;
12 | import com.amazonaws.services.dynamodbv2.model.BillingModeSummary;
13 | import org.apache.commons.logging.Log;
14 | import org.apache.commons.logging.LogFactory;
15 | import org.junit.Test;
16 |
17 | import com.amazonaws.services.dynamodbv2.model.DescribeTableResult;
18 | import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughputDescription;
19 | import com.amazonaws.services.dynamodbv2.model.TableDescription;
20 | import com.amazonaws.services.dynamodbv2.streamsadapter.util.TestRecordProcessorFactory;
21 | import com.amazonaws.services.dynamodbv2.streamsadapter.util.TestUtil;
22 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.InitialPositionInStream;
23 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;
24 |
25 | public class KinesisParametersTest extends FunctionalTestBase {
26 | private static final Log LOG = LogFactory.getLog(KinesisParametersTest.class);
27 |
28 | private static String KCL_WORKER_ID = "kcl-integration-test-worker";
29 | private static long IDLE_TIME_2S = 2000L;
30 |
31 | @Test
32 | public void leaseTableThroughputTest() throws Exception {
33 | KinesisClientLibConfiguration workerConfig =
34 | new KinesisClientLibConfiguration(leaseTable, streamId, credentials, KCL_WORKER_ID).withInitialPositionInStream(InitialPositionInStream.TRIM_HORIZON)
35 | .withInitialLeaseTableReadCapacity(50).withInitialLeaseTableWriteCapacity(50);
36 |
37 | startKCLWorker(workerConfig);
38 |
39 | while (((TestRecordProcessorFactory) recordProcessorFactory).getNumRecordsProcessed() < 0) {
40 | LOG.info("Sleep till RecordProcessor is initialized");
41 | Thread.sleep(THREAD_SLEEP_2S);
42 | }
43 |
44 | shutDownKCLWorker();
45 |
46 | DescribeTableResult describeTableResult = TestUtil.describeTable(dynamoDBClient, leaseTable);
47 | TableDescription leaseTableDescription = describeTableResult.getTable();
48 | ProvisionedThroughputDescription leaseTableThroughput = leaseTableDescription.getProvisionedThroughput();
49 |
50 | assertEquals(new Long(50), leaseTableThroughput.getReadCapacityUnits());
51 | assertEquals(new Long(50), leaseTableThroughput.getWriteCapacityUnits());
52 | }
53 |
54 | /**
55 | * This test configures KCL to call processRecords even when getRecords call returns nothing. The idle time setting
56 | * determines how many getRecords() calls will be made per second
57 | *
58 | * @throws Exception
59 | */
60 | @Test
61 | public void numProcessRecordsCallsTest() throws Exception {
62 | KinesisClientLibConfiguration workerConfig =
63 | new KinesisClientLibConfiguration(leaseTable, streamId, credentials, KCL_WORKER_ID).withMaxRecords(10).withInitialPositionInStream(InitialPositionInStream.TRIM_HORIZON)
64 | .withCallProcessRecordsEvenForEmptyRecordList(true).withIdleTimeBetweenReadsInMillis(IDLE_TIME_2S);
65 |
66 | startKCLWorker(workerConfig);
67 |
68 | while (((TestRecordProcessorFactory) recordProcessorFactory).getNumRecordsProcessed() < 0) {
69 | LOG.info("Sleep till RecordProcessor is initialized");
70 | Thread.sleep(THREAD_SLEEP_2S);
71 | }
72 |
73 | // Let KCL run for another 5 seconds
74 | Thread.sleep(THREAD_SLEEP_5S);
75 |
76 | shutDownKCLWorker();
77 |
78 | int numGetRecordsCalls = recordProcessorFactory.getNumProcessRecordsCalls();
79 |
80 | LOG.info("Num getRecords calls: " + numGetRecordsCalls);
81 | // Atleast 1 and atmost 2 getRecords/processRecords calls should have been made
82 | assertTrue(numGetRecordsCalls > 0 && numGetRecordsCalls <= 3);
83 | }
84 |
85 | /**
86 | * This test configures the worker with a non-default billing mode and ensures that the billing mode is passed
87 | * through to the created lease table.
88 | */
89 | @Test
90 | public void billingModeTest() throws Exception {
91 | KinesisClientLibConfiguration workerConfig =
92 | new KinesisClientLibConfiguration(leaseTable, streamId, credentials, KCL_WORKER_ID)
93 | .withBillingMode(BillingMode.PAY_PER_REQUEST);
94 |
95 | startKCLWorker(workerConfig);
96 |
97 | while (recordProcessorFactory.getNumRecordsProcessed() < 0) {
98 | LOG.info("Sleep till RecordProcessor is initialized");
99 | Thread.sleep(THREAD_SLEEP_2S);
100 | }
101 |
102 | shutDownKCLWorker();
103 |
104 | DescribeTableResult describeTableResult = TestUtil.describeTable(dynamoDBClient, leaseTable);
105 | TableDescription leaseTableDescription = describeTableResult.getTable();
106 | BillingModeSummary billingModeSummary = leaseTableDescription.getBillingModeSummary();
107 | assertEquals(BillingMode.PAY_PER_REQUEST.toString(), billingModeSummary.getBillingMode());
108 | }
109 | }
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/DescribeStreamRequestAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.mockito.Mockito.times;
10 | import static org.mockito.Mockito.verify;
11 | import static org.mockito.Mockito.when;
12 |
13 | import org.junit.Before;
14 | import org.junit.Test;
15 | import org.mockito.Mock;
16 | import org.mockito.MockitoAnnotations;
17 |
18 | import com.amazonaws.services.kinesis.model.DescribeStreamRequest;
19 |
20 | public class DescribeStreamRequestAdapterTest {
21 | private final String TEST_STRING = "TestString";
22 | private final Integer TEST_INT = 42;
23 |
24 | @Mock
25 | private DescribeStreamRequest mockRequest;
26 |
27 | private DescribeStreamRequestAdapter adapter;
28 |
29 | @Before
30 | public void setUpTest() {
31 | MockitoAnnotations.initMocks(this);
32 | adapter = new DescribeStreamRequestAdapter(mockRequest);
33 | }
34 |
35 | @Test
36 | public void testGetExclusiveStartShardId() {
37 | when(mockRequest.getExclusiveStartShardId()).thenReturn(TEST_STRING);
38 | String actual = adapter.getExclusiveStartShardId();
39 | assertEquals(TEST_STRING, actual);
40 | }
41 |
42 | @Test
43 | public void testSetExclusiveStartShardId() {
44 | adapter.setExclusiveStartShardId(TEST_STRING);
45 | verify(mockRequest, times(1)).setExclusiveStartShardId(TEST_STRING);
46 | }
47 |
48 | @Test
49 | public void testWithExclusiveStartShardId() {
50 | Object actual = adapter.withExclusiveStartShardId(TEST_STRING);
51 | assertEquals(adapter, actual);
52 | }
53 |
54 | @Test
55 | public void testGetLimit() {
56 | when(mockRequest.getLimit()).thenReturn(TEST_INT);
57 | Integer actual = adapter.getLimit();
58 | assertEquals(TEST_INT, actual);
59 | }
60 |
61 | @Test
62 | public void testSetLimit() {
63 | adapter.setLimit(TEST_INT);
64 | verify(mockRequest, times(1)).setLimit(TEST_INT);
65 | }
66 |
67 | @Test
68 | public void testWithLimit() {
69 | Object actual = adapter.withLimit(TEST_INT);
70 | assertEquals(adapter, actual);
71 | }
72 |
73 | @Test
74 | public void testGetStreamArn() {
75 | when(mockRequest.getStreamName()).thenReturn(TEST_STRING);
76 | String actual = adapter.getStreamArn();
77 | assertEquals(TEST_STRING, actual);
78 | }
79 |
80 | @Test
81 | public void testSetStreamArn() {
82 | adapter.setStreamArn(TEST_STRING);
83 | verify(mockRequest, times(1)).setStreamName(TEST_STRING);
84 | }
85 |
86 | @Test
87 | public void testWithStreamArn() {
88 | Object actual = adapter.withStreamArn(TEST_STRING);
89 | assertEquals(adapter, actual);
90 | }
91 |
92 | @Test
93 | public void testRealData() {
94 | DescribeStreamRequest request = createRequest();
95 | DescribeStreamRequestAdapter requestAdapter = new DescribeStreamRequestAdapter(request);
96 | assertEquals(request.getExclusiveStartShardId(), requestAdapter.getExclusiveStartShardId());
97 | assertEquals(request.getLimit(), requestAdapter.getLimit());
98 | assertEquals(request.getStreamName(), requestAdapter.getStreamArn());
99 | }
100 |
101 | private DescribeStreamRequest createRequest() {
102 | return new DescribeStreamRequest().withExclusiveStartShardId(TEST_STRING).withLimit(TEST_INT).withStreamName(TEST_STRING);
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/DescribeStreamResultAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertTrue;
9 | import static org.mockito.Mockito.when;
10 |
11 | import org.junit.Before;
12 | import org.junit.Test;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamResult;
17 | import com.amazonaws.services.dynamodbv2.model.StreamDescription;
18 |
19 | public class DescribeStreamResultAdapterTest {
20 |
21 | @Mock
22 | private DescribeStreamResult mockResult;
23 |
24 | @Mock
25 | private StreamDescription mockDescription;
26 |
27 | private DescribeStreamResultAdapter adapter;
28 |
29 | @Before
30 | public void setUpTest() {
31 | MockitoAnnotations.initMocks(this);
32 | when(mockResult.getStreamDescription()).thenReturn(mockDescription);
33 | adapter = new DescribeStreamResultAdapter(mockResult);
34 | }
35 |
36 | @Test
37 | public void testGetStreamDescription() {
38 | Object streamDescription = adapter.getStreamDescription();
39 | assertTrue(streamDescription instanceof StreamDescriptionAdapter);
40 | }
41 |
42 | @Test(expected = UnsupportedOperationException.class)
43 | public void testSetStreamDescription() {
44 | adapter.setStreamDescription(null);
45 | }
46 |
47 | @Test(expected = UnsupportedOperationException.class)
48 | public void testWithStreamDescription() {
49 | adapter.withStreamDescription(null);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetRecordsRequestAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.mockito.Mockito.times;
10 | import static org.mockito.Mockito.verify;
11 | import static org.mockito.Mockito.when;
12 |
13 | import org.junit.Before;
14 | import org.junit.Test;
15 | import org.mockito.Mock;
16 | import org.mockito.MockitoAnnotations;
17 |
18 | import com.amazonaws.services.kinesis.model.GetRecordsRequest;
19 |
20 | public class GetRecordsRequestAdapterTest {
21 | private final String TEST_STRING = "TestString";
22 | private final Integer TEST_INT = 42;
23 |
24 | @Mock
25 | private GetRecordsRequest mockRequest;
26 |
27 | private GetRecordsRequestAdapter adapter;
28 |
29 | @Before
30 | public void setUpTest() {
31 | MockitoAnnotations.initMocks(this);
32 | adapter = new GetRecordsRequestAdapter(mockRequest);
33 | }
34 |
35 | @Test
36 | public void testGetLimit() {
37 | when(mockRequest.getLimit()).thenReturn(TEST_INT);
38 | Integer actual = adapter.getLimit();
39 | assertEquals(TEST_INT, actual);
40 | }
41 |
42 | @Test
43 | public void testSetLimit() {
44 | adapter.setLimit(TEST_INT);
45 | verify(mockRequest, times(1)).setLimit(TEST_INT);
46 | }
47 |
48 | @Test
49 | public void testWithLimit() {
50 | Object actual = adapter.withLimit(TEST_INT);
51 | assertEquals(adapter, actual);
52 | }
53 |
54 | @Test
55 | public void testGetShardIterator() {
56 | when(mockRequest.getShardIterator()).thenReturn(TEST_STRING);
57 | String actual = adapter.getShardIterator();
58 | assertEquals(TEST_STRING, actual);
59 | }
60 |
61 | @Test
62 | public void testSetShardIterator() {
63 | adapter.setShardIterator(TEST_STRING);
64 | verify(mockRequest, times(1)).setShardIterator(TEST_STRING);
65 | }
66 |
67 | @Test
68 | public void testWithShardIterator() {
69 | Object actual = adapter.withShardIterator(TEST_STRING);
70 | assertEquals(adapter, actual);
71 | }
72 |
73 | @Test
74 | public void testRealData() {
75 | GetRecordsRequest request = createRequest();
76 | GetRecordsRequestAdapter requestAdapter = new GetRecordsRequestAdapter(request);
77 | assertEquals(request.getShardIterator(), requestAdapter.getShardIterator());
78 | assertEquals(request.getLimit(), requestAdapter.getLimit());
79 | }
80 |
81 | private GetRecordsRequest createRequest() {
82 | return new GetRecordsRequest().withLimit(TEST_INT).withShardIterator(TEST_STRING);
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetRecordsResultAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertTrue;
10 | import static org.mockito.Mockito.when;
11 |
12 | import java.util.Collection;
13 | import java.util.Collections;
14 |
15 | import org.junit.Before;
16 | import org.junit.Test;
17 | import org.mockito.Mock;
18 | import org.mockito.MockitoAnnotations;
19 |
20 | import com.amazonaws.services.dynamodbv2.model.GetRecordsResult;
21 | import com.amazonaws.services.dynamodbv2.model.Record;
22 |
23 | public class GetRecordsResultAdapterTest {
24 | private final String TEST_STRING = "TestString";
25 |
26 | @Mock
27 | private GetRecordsResult mockResult;
28 |
29 | private GetRecordsResultAdapter adapter;
30 |
31 | @Before
32 | public void setUpTest() {
33 | MockitoAnnotations.initMocks(this);
34 | java.util.List records = new java.util.ArrayList();
35 | records.add(new Record());
36 | when(mockResult.getRecords()).thenReturn(records);
37 | adapter = new GetRecordsResultAdapter(mockResult);
38 | }
39 |
40 | @Test
41 | public void testGetNextShardIterator() {
42 | when(mockResult.getNextShardIterator()).thenReturn(TEST_STRING);
43 | String actual = adapter.getNextShardIterator();
44 | assertEquals(TEST_STRING, actual);
45 | }
46 |
47 | @Test(expected = UnsupportedOperationException.class)
48 | public void testSetNextShardIterator() {
49 | adapter.setNextShardIterator(TEST_STRING);
50 | }
51 |
52 | @Test(expected = UnsupportedOperationException.class)
53 | public void testWithNextShardIterator() {
54 | adapter.withNextShardIterator(TEST_STRING);
55 | }
56 |
57 | @Test
58 | public void testGetRecordsWithItem() {
59 | java.util.List recordList = adapter.getRecords();
60 | assertEquals(1, recordList.size());
61 | assertTrue(recordList.get(0) instanceof RecordAdapter);
62 | }
63 |
64 | @Test
65 | public void testGetRecordsWithNoItems() {
66 | when(mockResult.getRecords()).thenReturn(new java.util.ArrayList());
67 | GetRecordsResultAdapter localAdapter = new GetRecordsResultAdapter(mockResult);
68 | java.util.List recordList = localAdapter.getRecords();
69 | assertEquals(0, recordList.size());
70 | }
71 |
72 | @Test
73 | public void testGetRecordsWithNull() {
74 | when(mockResult.getRecords()).thenReturn(null);
75 | GetRecordsResultAdapter localAdapter = new GetRecordsResultAdapter(mockResult);
76 | java.util.List recordList = localAdapter.getRecords();
77 | assertEquals(0, recordList.size());
78 | }
79 |
80 | @Test(expected = UnsupportedOperationException.class)
81 | public void testSetRecords() {
82 | adapter.setRecords(null);
83 | }
84 |
85 | @Test(expected = UnsupportedOperationException.class)
86 | public void testWithRecords() {
87 | adapter.withRecords(null, null);
88 | }
89 |
90 | @Test(expected = UnsupportedOperationException.class)
91 | public void testWithRecords2() {
92 | Collection records = Collections.emptyList();
93 | adapter.withRecords(records);
94 | }
95 |
96 | @Test
97 | public void testRealDataNoRecords() {
98 | GetRecordsResult result = createResult(false);
99 | GetRecordsResultAdapter resultAdapter = new GetRecordsResultAdapter(result);
100 | assertEquals(result.getNextShardIterator(), resultAdapter.getNextShardIterator());
101 | assertEquals(result.getRecords().size(), resultAdapter.getRecords().size());
102 | }
103 |
104 | @Test
105 | public void testRealDataWithRecords() {
106 | GetRecordsResult result = createResult(true);
107 | GetRecordsResultAdapter resultAdapter = new GetRecordsResultAdapter(result);
108 | assertEquals(result.getNextShardIterator(), resultAdapter.getNextShardIterator());
109 | assertEquals(result.getRecords().size(), resultAdapter.getRecords().size());
110 | }
111 |
112 | private GetRecordsResult createResult(Boolean withRecord) {
113 | java.util.List records = new java.util.ArrayList();
114 | if (withRecord) {
115 | records.add(new Record());
116 | }
117 | return new GetRecordsResult().withRecords(records).withNextShardIterator(TEST_STRING);
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetShardIteratorRequestAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.mockito.Mockito.times;
10 | import static org.mockito.Mockito.verify;
11 | import static org.mockito.Mockito.when;
12 |
13 | import org.junit.Before;
14 | import org.junit.Test;
15 | import org.mockito.Mock;
16 | import org.mockito.MockitoAnnotations;
17 |
18 | import com.amazonaws.services.kinesis.model.GetShardIteratorRequest;
19 |
20 | public class GetShardIteratorRequestAdapterTest {
21 | private final String TEST_STRING = "TestString";
22 |
23 | @Mock
24 | private GetShardIteratorRequest mockRequest;
25 |
26 | private GetShardIteratorRequestAdapter adapter;
27 |
28 | @Before
29 | public void setUpTest() {
30 | MockitoAnnotations.initMocks(this);
31 | adapter = new GetShardIteratorRequestAdapter(mockRequest);
32 | }
33 |
34 | @Test
35 | public void testGetStreamArn() {
36 | when(mockRequest.getStreamName()).thenReturn(TEST_STRING);
37 | String actual = adapter.getStreamArn();
38 | assertEquals(TEST_STRING, actual);
39 | }
40 |
41 | @Test
42 | public void testSetStreamArn() {
43 | adapter.setStreamArn(TEST_STRING);
44 | verify(mockRequest, times(1)).setStreamName(TEST_STRING);
45 | }
46 |
47 | @Test
48 | public void testWithStreamArn() {
49 | Object actual = adapter.withStreamArn(TEST_STRING);
50 | assertEquals(adapter, actual);
51 | }
52 |
53 | @Test
54 | public void testGetShardId() {
55 | when(mockRequest.getShardId()).thenReturn(TEST_STRING);
56 | String actual = adapter.getShardId();
57 | assertEquals(TEST_STRING, actual);
58 | }
59 |
60 | @Test
61 | public void testSetShardId() {
62 | adapter.setShardId(TEST_STRING);
63 | verify(mockRequest, times(1)).setShardId(TEST_STRING);
64 | }
65 |
66 | @Test
67 | public void testWithShardId() {
68 | Object actual = adapter.withShardId(TEST_STRING);
69 | assertEquals(adapter, actual);
70 | }
71 |
72 | @Test
73 | public void testGetSequenceNumber() {
74 | when(mockRequest.getStartingSequenceNumber()).thenReturn(TEST_STRING);
75 | String actual = adapter.getSequenceNumber();
76 | assertEquals(TEST_STRING, actual);
77 | }
78 |
79 | @Test
80 | public void testSetSequenceNumber() {
81 | adapter.setSequenceNumber(TEST_STRING);
82 | verify(mockRequest, times(1)).setStartingSequenceNumber(TEST_STRING);
83 | }
84 |
85 | @Test
86 | public void testWithSequenceNumber() {
87 | Object actual = adapter.withSequenceNumber(TEST_STRING);
88 | assertEquals(adapter, actual);
89 | }
90 |
91 | @Test
92 | public void testGetShardIteratorType() {
93 | when(mockRequest.getShardIteratorType()).thenReturn(TEST_STRING);
94 | String actual = adapter.getShardIteratorType();
95 | assertEquals(TEST_STRING, actual);
96 | }
97 |
98 | @Test(expected = IllegalArgumentException.class)
99 | public void testSetShardIteratorTypeFailure() {
100 | adapter.setShardIteratorType(TEST_STRING);
101 | }
102 |
103 | @Test
104 | public void testSetShardIteratorTypeAsTypeAfterSequenceNumber() {
105 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER);
106 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString());
107 | }
108 |
109 | @Test
110 | public void testSetShardIteratorTypeAsTypeAtSequenceNumber() {
111 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.AT_SEQUENCE_NUMBER);
112 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.AT_SEQUENCE_NUMBER.toString());
113 | }
114 |
115 | @Test
116 | public void testSetShardIteratorTypeAsTypeLatest() {
117 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.LATEST);
118 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.LATEST.toString());
119 | }
120 |
121 | @Test
122 | public void testSetShardIteratorTypeAsTypeTrimHorizon() {
123 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.TRIM_HORIZON);
124 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.TRIM_HORIZON.toString());
125 | }
126 |
127 | @Test(expected = IllegalArgumentException.class)
128 | public void testWithShardIteratorTypeFailure() {
129 | adapter.withShardIteratorType(TEST_STRING);
130 | }
131 |
132 | @Test
133 | public void testWithShardIteratorTypeAsStringAfterSequenceNumber() {
134 | adapter.withShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString());
135 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString());
136 | }
137 |
138 | @Test
139 | public void testWithShardIteratorTypeAsStringAtSequenceNumber() {
140 | adapter.withShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.AT_SEQUENCE_NUMBER.toString());
141 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.AT_SEQUENCE_NUMBER.toString());
142 | }
143 |
144 | @Test
145 | public void testWithShardIteratorTypeAsStringLatest() {
146 | adapter.withShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.LATEST.toString());
147 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.LATEST.toString());
148 | }
149 |
150 | @Test
151 | public void testWithShardIteratorTypeAsStringTrimHorizon() {
152 | adapter.withShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.TRIM_HORIZON.toString());
153 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.TRIM_HORIZON.toString());
154 | }
155 |
156 | @Test
157 | public void testSetShardIteratorTypeAsStringAfterSequenceNumber() {
158 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString());
159 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.AFTER_SEQUENCE_NUMBER.toString());
160 | }
161 |
162 | @Test
163 | public void testSetShardIteratorTypeAsStringAtSequenceNumber() {
164 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.AT_SEQUENCE_NUMBER.toString());
165 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.AT_SEQUENCE_NUMBER.toString());
166 | }
167 |
168 | @Test
169 | public void testSetShardIteratorTypeAsStringLatest() {
170 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.LATEST.toString());
171 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.LATEST.toString());
172 | }
173 |
174 | @Test
175 | public void testSetShardIteratorTypeAsStringTrimHorizon() {
176 | adapter.setShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.TRIM_HORIZON.toString());
177 | verify(mockRequest, times(1)).setShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.TRIM_HORIZON.toString());
178 | }
179 |
180 | @Test
181 | public void testWithShardIteratorTypeAsType() {
182 | Object actual = adapter.withShardIteratorType(com.amazonaws.services.dynamodbv2.model.ShardIteratorType.LATEST);
183 | assertEquals(adapter, actual);
184 | }
185 |
186 | @Test
187 | public void testRealData() {
188 | GetShardIteratorRequest request = createRequest();
189 | GetShardIteratorRequestAdapter requestAdapter = new GetShardIteratorRequestAdapter(request);
190 | assertEquals(request.getStartingSequenceNumber(), requestAdapter.getSequenceNumber());
191 | assertEquals(request.getShardId(), requestAdapter.getShardId());
192 | assertEquals(request.getShardIteratorType(), requestAdapter.getShardIteratorType());
193 | assertEquals(request.getStreamName(), requestAdapter.getStreamArn());
194 | }
195 |
196 | private GetShardIteratorRequest createRequest() {
197 | return new GetShardIteratorRequest()
198 | .withShardId(TEST_STRING)
199 | .withStartingSequenceNumber(TEST_STRING)
200 | .withShardIteratorType(com.amazonaws.services.kinesis.model.ShardIteratorType.LATEST)
201 | .withStreamName(TEST_STRING);
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/GetShardIteratorResultAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.mockito.Mockito.when;
10 |
11 | import org.junit.Before;
12 | import org.junit.Test;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult;
17 |
18 | public class GetShardIteratorResultAdapterTest {
19 | private final String TEST_STRING = "TestString";
20 |
21 | @Mock
22 | private GetShardIteratorResult mockResult;
23 |
24 | private GetShardIteratorResultAdapter adapter;
25 |
26 | @Before
27 | public void setUpTest() {
28 | MockitoAnnotations.initMocks(this);
29 | adapter = new GetShardIteratorResultAdapter(mockResult);
30 | }
31 |
32 | @Test
33 | public void testGetShardIterator() {
34 | when(mockResult.getShardIterator()).thenReturn(TEST_STRING);
35 | String actual = adapter.getShardIterator();
36 | assertEquals(TEST_STRING, actual);
37 | }
38 |
39 | @Test(expected = UnsupportedOperationException.class)
40 | public void testSetShardIterator() {
41 | adapter.setShardIterator(TEST_STRING);
42 | }
43 |
44 | @Test(expected = UnsupportedOperationException.class)
45 | public void testWithShardIterator() {
46 | adapter.withShardIterator(TEST_STRING);
47 | }
48 |
49 | @Test
50 | public void testRealData() {
51 | GetShardIteratorResult result = createResult();
52 | GetShardIteratorResultAdapter resultAdapter = new GetShardIteratorResultAdapter(result);
53 | assertEquals(result.getShardIterator(), resultAdapter.getShardIterator());
54 | }
55 |
56 | private GetShardIteratorResult createResult() {
57 | return new GetShardIteratorResult().withShardIterator(TEST_STRING);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/ListStreamsRequestAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.mockito.Mockito.verify;
10 | import static org.mockito.Mockito.when;
11 |
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.mockito.Mock;
15 | import org.mockito.MockitoAnnotations;
16 |
17 | import com.amazonaws.services.kinesis.model.ListStreamsRequest;
18 |
19 | public class ListStreamsRequestAdapterTest {
20 | private final String TEST_STRING = "TestString";
21 |
22 | private final Integer TEST_INT = 42;
23 |
24 | @Mock
25 | private ListStreamsRequest mockRequest;
26 |
27 | private ListStreamsRequestAdapter adapter;
28 |
29 | @Before
30 | public void setUpTest() {
31 | MockitoAnnotations.initMocks(this);
32 | adapter = new ListStreamsRequestAdapter(mockRequest);
33 | }
34 |
35 | @Test
36 | public void testGetExclusiveStartStreamArn() {
37 | when(mockRequest.getExclusiveStartStreamName()).thenReturn(TEST_STRING);
38 | String actual = adapter.getExclusiveStartStreamArn();
39 | assertEquals(TEST_STRING, actual);
40 | }
41 |
42 | @Test
43 | public void testSetExclusiveStartStreamArn() {
44 | adapter.setExclusiveStartStreamArn(TEST_STRING);
45 | verify(mockRequest).setExclusiveStartStreamName(TEST_STRING);
46 | }
47 |
48 | @Test
49 | public void testWithExclusiveStartStreamArn() {
50 | Object actual = adapter.withExclusiveStartStreamArn(TEST_STRING);
51 | assertEquals(adapter, actual);
52 | }
53 |
54 | @Test(expected = UnsupportedOperationException.class)
55 | public void testGetTableName() {
56 | adapter.getTableName();
57 | }
58 |
59 | @Test(expected = UnsupportedOperationException.class)
60 | public void testSetTableName() {
61 | adapter.setTableName(TEST_STRING);
62 | }
63 |
64 | @Test(expected = UnsupportedOperationException.class)
65 | public void testWithTableName() {
66 | adapter.withTableName(TEST_STRING);
67 | }
68 |
69 | @Test
70 | public void testGetLimit() {
71 | when(mockRequest.getLimit()).thenReturn(TEST_INT);
72 | Integer actual = adapter.getLimit();
73 | assertEquals(TEST_INT, actual);
74 | }
75 |
76 | @Test
77 | public void testSetLimit() {
78 | adapter.setLimit(TEST_INT);
79 | verify(mockRequest).setLimit(TEST_INT);
80 | }
81 |
82 | @Test
83 | public void testWithLimit() {
84 | Object actual = adapter.withLimit(TEST_INT);
85 | assertEquals(adapter, actual);
86 | }
87 |
88 | @Test
89 | public void testRealData() {
90 | ListStreamsRequest request = createRequest();
91 | ListStreamsRequestAdapter requestAdapter = new ListStreamsRequestAdapter(request);
92 | assertEquals(request.getExclusiveStartStreamName(), requestAdapter.getExclusiveStartStreamArn());
93 | assertEquals(request.getLimit(), requestAdapter.getLimit());
94 | }
95 |
96 | private ListStreamsRequest createRequest() {
97 | return new ListStreamsRequest().withExclusiveStartStreamName(TEST_STRING).withLimit(TEST_INT);
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/ListStreamsResultAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertFalse;
10 | import static org.junit.Assert.assertTrue;
11 | import static org.mockito.Mockito.when;
12 |
13 | import java.util.ArrayList;
14 | import java.util.Collection;
15 | import java.util.Collections;
16 | import java.util.List;
17 |
18 | import org.junit.Before;
19 | import org.junit.Test;
20 | import org.mockito.Mock;
21 | import org.mockito.MockitoAnnotations;
22 |
23 | import com.amazonaws.services.dynamodbv2.model.ListStreamsResult;
24 | import com.amazonaws.services.dynamodbv2.model.Stream;
25 |
26 | public class ListStreamsResultAdapterTest {
27 | private final String TEST_STRING = "TestString";
28 |
29 | @Mock
30 | private ListStreamsResult mockResult;
31 |
32 | private ListStreamsResultAdapter adapter;
33 |
34 | @Before
35 | public void setUpTest() {
36 | MockitoAnnotations.initMocks(this);
37 | adapter = new ListStreamsResultAdapter(mockResult);
38 | }
39 |
40 | @Test
41 | public void testGetStreamNamesWithNoItems() {
42 | when(mockResult.getStreams()).thenReturn(new java.util.ArrayList());
43 | java.util.List actual = adapter.getStreamNames();
44 | assertTrue(actual.isEmpty());
45 | }
46 |
47 | @Test
48 | public void testGetStreamNamesWithItem() {
49 | java.util.List streamList = new java.util.ArrayList<>();
50 | Stream stream = new Stream();
51 | stream.setStreamArn(TEST_STRING);
52 | streamList.add(stream);
53 | when(mockResult.getStreams()).thenReturn(streamList);
54 |
55 | java.util.List actual = adapter.getStreamNames();
56 | assertTrue(actual.size() == 1);
57 | assertEquals(TEST_STRING, actual.get(0));
58 | }
59 |
60 | @Test(expected = UnsupportedOperationException.class)
61 | public void testSetStreamNames() {
62 | adapter.setStreamNames(new java.util.ArrayList());
63 | }
64 |
65 | @Test(expected = UnsupportedOperationException.class)
66 | public void testWithStreamNames() {
67 | adapter.withStreamNames(null, null);
68 | }
69 |
70 | @Test(expected = UnsupportedOperationException.class)
71 | public void testWithStreamNames2() {
72 | final Collection streamNames = Collections.emptyList();
73 | adapter.withStreamNames(streamNames);
74 | }
75 |
76 | @Test
77 | public void testGetHasMoreStreamsTrue() {
78 | when(mockResult.getLastEvaluatedStreamArn()).thenReturn(TEST_STRING);
79 | assertTrue(adapter.getHasMoreStreams());
80 | }
81 |
82 | @Test
83 | public void testGetHasMoreStreamsFalse() {
84 | when(mockResult.getLastEvaluatedStreamArn()).thenReturn(null);
85 | assertFalse(adapter.getHasMoreStreams());
86 | }
87 |
88 | @Test
89 | public void testIsHasMoreStreamsTrue() {
90 | when(mockResult.getLastEvaluatedStreamArn()).thenReturn(TEST_STRING);
91 | assertTrue(adapter.isHasMoreStreams());
92 | }
93 |
94 | @Test
95 | public void testIsHasMoreStreamsFalse() {
96 | when(mockResult.getLastEvaluatedStreamArn()).thenReturn(null);
97 | assertFalse(adapter.isHasMoreStreams());
98 | }
99 |
100 | @Test(expected = UnsupportedOperationException.class)
101 | public void testSetHasMoreStreams() {
102 | adapter.setHasMoreStreams(false);
103 | }
104 |
105 | @Test(expected = UnsupportedOperationException.class)
106 | public void testWithHasMoreStreams() {
107 | adapter.withHasMoreStreams(false);
108 | }
109 |
110 | @Test
111 | public void testRealDataNoIds() {
112 | ListStreamsResult result = createResult(false);
113 | ListStreamsResultAdapter resultAdapter = new ListStreamsResultAdapter(result);
114 | List streamArns = extractStreamArns(result);
115 | assertEquals(streamArns, resultAdapter.getStreamNames());
116 | }
117 |
118 | @Test
119 | public void testRealDataWithIds() {
120 | ListStreamsResult result = createResult(true);
121 | ListStreamsResultAdapter resultAdapter = new ListStreamsResultAdapter(result);
122 | assertEquals(extractStreamArns(result), resultAdapter.getStreamNames());
123 | }
124 |
125 | private List extractStreamArns(ListStreamsResult result) {
126 | List streams = result.getStreams();
127 | List streamArns = new ArrayList<>(streams.size());
128 | for (Stream stream : streams) {
129 | streamArns.add(stream.getStreamArn());
130 | }
131 | return streamArns;
132 | }
133 |
134 | private ListStreamsResult createResult(Boolean withArns) {
135 | java.util.List streams = new java.util.ArrayList<>();
136 | if (withArns) {
137 | Stream stream = new Stream();
138 | stream.setStreamArn(TEST_STRING);
139 | streams.add(stream);
140 | }
141 | return new ListStreamsResult().withStreams(streams).withLastEvaluatedStreamArn(TEST_STRING);
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/RecordAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertNull;
10 | import static org.mockito.Mockito.times;
11 | import static org.mockito.Mockito.verify;
12 | import static org.powermock.api.mockito.PowerMockito.mock;
13 | import static org.powermock.api.mockito.PowerMockito.when;
14 |
15 | import java.io.IOException;
16 | import java.nio.ByteBuffer;
17 | import java.util.Date;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 | import java.util.UUID;
21 |
22 | import org.junit.Before;
23 | import org.junit.Test;
24 | import org.junit.runner.RunWith;
25 | import org.powermock.core.classloader.annotations.PrepareForTest;
26 | import org.powermock.modules.junit4.PowerMockRunner;
27 | import org.powermock.reflect.Whitebox;
28 |
29 | import com.amazonaws.services.dynamodbv2.model.AttributeValue;
30 | import com.amazonaws.services.dynamodbv2.model.OperationType;
31 | import com.amazonaws.services.dynamodbv2.model.Record;
32 | import com.amazonaws.services.dynamodbv2.model.StreamRecord;
33 | import com.amazonaws.services.dynamodbv2.model.StreamViewType;
34 | import com.fasterxml.jackson.core.JsonParseException;
35 | import com.fasterxml.jackson.core.JsonProcessingException;
36 | import com.fasterxml.jackson.databind.JsonMappingException;
37 | import com.fasterxml.jackson.databind.ObjectMapper;
38 |
39 | @PrepareForTest({ObjectMapper.class, RecordAdapter.class})
40 | @RunWith(PowerMockRunner.class)
41 | public class RecordAdapterTest {
42 |
43 | private static final ObjectMapper MAPPER = new RecordObjectMapper();
44 |
45 | private static final ObjectMapper MOCK_MAPPER = mock(RecordObjectMapper.class);
46 |
47 | private static final String TEST_STRING = "TestString";
48 |
49 | private static final Date TEST_DATE = new Date(1156377600 /* EC2 Announced */);
50 |
51 | private static final String TEST_RECORD_v1_0 =
52 | new StringBuilder().append("{").append("\"awsRegion\":\"us-east-1\",").append("\"dynamodb\":").append("{").append("\"Keys\":").append("{")
53 | .append("\"hashKey\":{\"S\":\"hashKeyValue\"}").append("},").append("\"StreamViewType\":\"NEW_AND_OLD_IMAGES\",")
54 | .append("\"SequenceNumber\":\"100000000003498069978\",").append("\"SizeBytes\":6").append("},").append("\"eventID\":\"33fe21d365c03362c5e66d8dec2b63d5\",")
55 | .append("\"eventVersion\":\"1.0\",").append("\"eventName\":\"INSERT\",").append("\"eventSource\":\"aws:dynamodb\"").append("}").toString();
56 |
57 | private Record testRecord;
58 |
59 | private RecordAdapter adapter;
60 |
61 | @Before
62 | public void setUpTest() {
63 | testRecord = new Record();
64 | testRecord.setAwsRegion("us-east-1");
65 | testRecord.setEventID(UUID.randomUUID().toString());
66 | testRecord.setEventSource("aws:dynamodb");
67 | testRecord.setEventVersion("1.1");
68 | testRecord.setEventName(OperationType.MODIFY);
69 | StreamRecord testStreamRecord = new StreamRecord();
70 | testRecord.setDynamodb(testStreamRecord);
71 | Map key = new HashMap();
72 | key.put("hashKey", new AttributeValue("hashKeyValue"));
73 | Map oldImage = new HashMap(key);
74 | Map newImage = new HashMap(key);
75 | newImage.put("newAttributeKey", new AttributeValue("someValue"));
76 | testStreamRecord.setApproximateCreationDateTime(TEST_DATE);
77 | testStreamRecord.setKeys(key);
78 | testStreamRecord.setOldImage(oldImage);
79 | testStreamRecord.setNewImage(newImage);
80 | testStreamRecord.setSizeBytes(Long.MAX_VALUE);
81 | testStreamRecord.setSequenceNumber(UUID.randomUUID().toString());
82 | testStreamRecord.setStreamViewType(StreamViewType.NEW_AND_OLD_IMAGES);
83 | testStreamRecord.setSequenceNumber(TEST_STRING);
84 | adapter = new RecordAdapter(testRecord);
85 | }
86 |
87 | @Test
88 | public void testDoesNotGenerateBytesWhenGenerateDataBytesIsFalse() {
89 | adapter = new RecordAdapter(testRecord, false);
90 | assertEquals(0, adapter.getData().array().length);
91 | }
92 |
93 | @Test
94 | public void testGetSequenceNumber() {
95 | String actual = adapter.getSequenceNumber();
96 | assertEquals(TEST_STRING, actual);
97 | }
98 |
99 | @Test(expected = UnsupportedOperationException.class)
100 | public void testSetSequenceNumber() {
101 | adapter.setSequenceNumber(TEST_STRING);
102 | }
103 |
104 | @Test(expected = UnsupportedOperationException.class)
105 | public void testWithSequenceNumber() {
106 | adapter.withSequenceNumber(TEST_STRING);
107 | }
108 |
109 | @Test
110 | public void testGetData() throws JsonProcessingException {
111 | Whitebox.setInternalState(RecordAdapter.class, ObjectMapper.class, MOCK_MAPPER);
112 | when(MOCK_MAPPER.writeValueAsString(adapter.getInternalObject())).thenReturn(MAPPER.writeValueAsString(adapter.getInternalObject()));
113 | ByteBuffer data = ByteBuffer.wrap(MAPPER.writeValueAsString(adapter.getInternalObject()).getBytes());
114 | assertEquals(data, adapter.getData());
115 | // Retrieve data twice to validate it is only deserialized once
116 | assertEquals(data, adapter.getData());
117 | verify(MOCK_MAPPER, times(1)).writeValueAsString(adapter.getInternalObject());
118 | }
119 |
120 | @Test(expected = RuntimeException.class)
121 | public void testGetDataMappingException() throws JsonProcessingException {
122 | Whitebox.setInternalState(RecordAdapter.class, ObjectMapper.class, MOCK_MAPPER);
123 | when(MOCK_MAPPER.writeValueAsString(adapter.getInternalObject())).thenThrow(mock(JsonProcessingException.class));
124 | adapter.getData();
125 | }
126 |
127 | /**
128 | * We need a custom serializer/deserializer to be able to process Record object because of the conflicts that arise
129 | * with the standard jackson mapper for fields like eventName etc.
130 | */
131 | @Test
132 | public void testGetDataDeserialized() throws JsonParseException, JsonMappingException, IOException {
133 | Whitebox.setInternalState(RecordAdapter.class, ObjectMapper.class, MAPPER);
134 |
135 | java.nio.ByteBuffer data = adapter.getData();
136 | Record actual = MAPPER.readValue(data.array(), Record.class);
137 | assertEquals(adapter.getInternalObject(), actual);
138 | }
139 |
140 | @Test(expected = UnsupportedOperationException.class)
141 | public void testSetData() {
142 | adapter.setData(null);
143 | }
144 |
145 | @Test(expected = UnsupportedOperationException.class)
146 | public void testWithData() {
147 | adapter.withData(null);
148 | }
149 |
150 | @Test
151 | public void testGetPartitionKey() {
152 | assertEquals(adapter.getPartitionKey(), null);
153 | }
154 |
155 | @Test(expected = UnsupportedOperationException.class)
156 | public void testSetPartitionKey() {
157 | adapter.setPartitionKey(TEST_STRING);
158 | }
159 |
160 | @Test(expected = UnsupportedOperationException.class)
161 | public void testWithPartitionKey() {
162 | adapter.withPartitionKey(TEST_STRING);
163 | }
164 |
165 | @Test
166 | public void testApproximateCreationDateTime() throws IOException {
167 | String serialized = MAPPER.writeValueAsString(testRecord);
168 | Record deserialized = MAPPER.readValue(serialized, Record.class);
169 | com.amazonaws.services.kinesis.model.Record adapter = new RecordAdapter(deserialized);
170 | assertEquals(TEST_DATE, adapter.getApproximateArrivalTimestamp());
171 | Date newDate = new Date();
172 | adapter.setApproximateArrivalTimestamp(newDate);
173 | assertEquals(newDate, deserialized.getDynamodb().getApproximateCreationDateTime());
174 | assertEquals(newDate, adapter.getApproximateArrivalTimestamp());
175 | adapter.withApproximateArrivalTimestamp(TEST_DATE);
176 | assertEquals(TEST_DATE, deserialized.getDynamodb().getApproximateCreationDateTime());
177 | assertEquals(TEST_DATE, adapter.getApproximateArrivalTimestamp());
178 | }
179 |
180 | @Test
181 | public void testGetInternalObject() {
182 | com.amazonaws.services.kinesis.model.Record kinesisRecord = null;
183 | kinesisRecord = new RecordAdapter(testRecord);
184 | Record internalObject = ((RecordAdapter) kinesisRecord).getInternalObject();
185 | assertEquals(testRecord, internalObject);
186 | }
187 |
188 | @Test
189 | public void testRecord_v1_0() throws IOException {
190 | Record deserialized = MAPPER.readValue(TEST_RECORD_v1_0, Record.class);
191 | com.amazonaws.services.kinesis.model.Record adapter = new RecordAdapter(deserialized);
192 | assertNull(adapter.getApproximateArrivalTimestamp());
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/ShardAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.mockito.Mockito.when;
10 |
11 | import org.junit.Before;
12 | import org.junit.Test;
13 | import org.mockito.Mock;
14 | import org.mockito.MockitoAnnotations;
15 |
16 | import com.amazonaws.services.dynamodbv2.model.SequenceNumberRange;
17 | import com.amazonaws.services.dynamodbv2.model.Shard;
18 |
19 | public class ShardAdapterTest {
20 | private final String TEST_STRING = "TestString";
21 |
22 | @Mock
23 | private Shard mockShard;
24 |
25 | @Mock
26 | private SequenceNumberRange mockSequenceNumberRange;
27 |
28 | private ShardAdapter adapter;
29 |
30 | @Before
31 | public void setUpTest() {
32 | MockitoAnnotations.initMocks(this);
33 | adapter = new ShardAdapter(mockShard);
34 | when(mockShard.getSequenceNumberRange()).thenReturn(mockSequenceNumberRange);
35 | }
36 |
37 | @Test
38 | public void testGetShardId() {
39 | when(mockShard.getShardId()).thenReturn(TEST_STRING);
40 | String actual = adapter.getShardId();
41 | assertEquals(TEST_STRING, actual);
42 | }
43 |
44 | @Test(expected = UnsupportedOperationException.class)
45 | public void testSetShardId() {
46 | adapter.setShardId(TEST_STRING);
47 | }
48 |
49 | @Test(expected = UnsupportedOperationException.class)
50 | public void testWithShardId() {
51 | adapter.withShardId(TEST_STRING);
52 | }
53 |
54 | @Test
55 | public void testGetParentShardId() {
56 | when(mockShard.getParentShardId()).thenReturn(TEST_STRING);
57 | String actual = adapter.getParentShardId();
58 | assertEquals(TEST_STRING, actual);
59 | }
60 |
61 | @Test(expected = UnsupportedOperationException.class)
62 | public void testSetParentShardId() {
63 | adapter.setParentShardId(TEST_STRING);
64 | }
65 |
66 | @Test(expected = UnsupportedOperationException.class)
67 | public void testWithParentShardId() {
68 | adapter.withParentShardId(TEST_STRING);
69 | }
70 |
71 | @Test
72 | public void testGetAdjacentParentShardId() {
73 | String actual = adapter.getAdjacentParentShardId();
74 | assertEquals(null, actual);
75 | }
76 |
77 | @Test(expected = UnsupportedOperationException.class)
78 | public void testSetAdjacentParentShardId() {
79 | adapter.setAdjacentParentShardId(TEST_STRING);
80 | }
81 |
82 | @Test(expected = UnsupportedOperationException.class)
83 | public void testWithAdjacentParentShardId() {
84 | adapter.withAdjacentParentShardId(TEST_STRING);
85 | }
86 |
87 | @Test
88 | public void testGetHashKeyRange() {
89 | com.amazonaws.services.kinesis.model.HashKeyRange hashKeyRange = adapter.getHashKeyRange();
90 | assertEquals(java.math.BigInteger.ZERO.toString(), hashKeyRange.getStartingHashKey());
91 | assertEquals(java.math.BigInteger.ONE.toString(), hashKeyRange.getEndingHashKey());
92 | }
93 |
94 | @Test(expected = UnsupportedOperationException.class)
95 | public void testSetHashKeyRange() {
96 | adapter.setHashKeyRange(null);
97 | }
98 |
99 | @Test(expected = UnsupportedOperationException.class)
100 | public void testWithHashKeyRange() {
101 | adapter.withHashKeyRange(null);
102 | }
103 |
104 | @Test
105 | public void testGetSequenceNumberRange() {
106 | when(mockSequenceNumberRange.getStartingSequenceNumber()).thenReturn(TEST_STRING);
107 | when(mockSequenceNumberRange.getEndingSequenceNumber()).thenReturn(TEST_STRING);
108 | com.amazonaws.services.kinesis.model.SequenceNumberRange sequenceNumberRange = adapter.getSequenceNumberRange();
109 | assertEquals(TEST_STRING, sequenceNumberRange.getStartingSequenceNumber());
110 | assertEquals(TEST_STRING, sequenceNumberRange.getEndingSequenceNumber());
111 | }
112 |
113 | @Test(expected = UnsupportedOperationException.class)
114 | public void testSetSequenceNumberRange() {
115 | adapter.setSequenceNumberRange(null);
116 | }
117 |
118 | @Test(expected = UnsupportedOperationException.class)
119 | public void testWithSequenceNumberRange() {
120 | adapter.withSequenceNumberRange(null);
121 | }
122 |
123 | @Test
124 | public void testRealData() {
125 | Shard shard = createShard();
126 | ShardAdapter shardAdapter = new ShardAdapter(shard);
127 | assertEquals(shard.getShardId(), shardAdapter.getShardId());
128 | assertEquals(shard.getParentShardId(), shardAdapter.getParentShardId());
129 | assertEquals(shard.getSequenceNumberRange().getStartingSequenceNumber(), shardAdapter.getSequenceNumberRange().getStartingSequenceNumber());
130 | assertEquals(shard.getSequenceNumberRange().getEndingSequenceNumber(), shardAdapter.getSequenceNumberRange().getEndingSequenceNumber());
131 | }
132 |
133 | private Shard createShard() {
134 | return new Shard().withShardId(TEST_STRING).withParentShardId(TEST_STRING)
135 | .withSequenceNumberRange(new SequenceNumberRange().withStartingSequenceNumber(TEST_STRING).withEndingSequenceNumber(TEST_STRING));
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/model/StreamDescriptionAdapterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.model;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertFalse;
10 | import static org.junit.Assert.assertSame;
11 | import static org.junit.Assert.assertTrue;
12 | import static org.mockito.Mockito.when;
13 |
14 | import java.util.Collection;
15 | import java.util.Collections;
16 |
17 | import org.junit.Before;
18 | import org.junit.Test;
19 | import org.mockito.Mock;
20 | import org.mockito.MockitoAnnotations;
21 |
22 | import com.amazonaws.services.dynamodbv2.model.Shard;
23 | import com.amazonaws.services.dynamodbv2.model.StreamDescription;
24 | import com.amazonaws.services.dynamodbv2.model.StreamStatus;
25 |
26 | public class StreamDescriptionAdapterTest {
27 | private final String TEST_STRING = "TestString";
28 |
29 | @Mock
30 | private StreamDescription mockDescription;
31 |
32 | private StreamDescriptionAdapter adapter;
33 |
34 | @Before
35 | public void setUpTest() {
36 | MockitoAnnotations.initMocks(this);
37 | java.util.List shards = new java.util.ArrayList();
38 | shards.add(new Shard());
39 | when(mockDescription.getShards()).thenReturn(shards);
40 | adapter = new StreamDescriptionAdapter(mockDescription);
41 | }
42 |
43 | @Test
44 | public void testGetStreamName() {
45 | when(mockDescription.getStreamArn()).thenReturn(TEST_STRING);
46 | String actual = adapter.getStreamName();
47 | assertEquals(TEST_STRING, actual);
48 | }
49 |
50 | @Test(expected = UnsupportedOperationException.class)
51 | public void testSetStreamName() {
52 | adapter.setStreamName(TEST_STRING);
53 | }
54 |
55 | @Test(expected = UnsupportedOperationException.class)
56 | public void testWithStreamName() {
57 | adapter.withStreamName(TEST_STRING);
58 | }
59 |
60 | @Test
61 | public void testGetStreamARN() {
62 | when(mockDescription.getStreamArn()).thenReturn(TEST_STRING);
63 | String actual = adapter.getStreamARN();
64 | assertEquals(TEST_STRING, actual);
65 | }
66 |
67 | @Test(expected = UnsupportedOperationException.class)
68 | public void testSetStreamARN() {
69 | adapter.setStreamARN(TEST_STRING);
70 | }
71 |
72 | @Test(expected = UnsupportedOperationException.class)
73 | public void testWithStreamARN() {
74 | adapter.withStreamARN(TEST_STRING);
75 | }
76 |
77 | @Test
78 | public void testGetStreamStatus() {
79 | when(mockDescription.getStreamStatus()).thenReturn(StreamStatus.ENABLING.toString());
80 | String actual = adapter.getStreamStatus();
81 | assertEquals(com.amazonaws.services.kinesis.model.StreamStatus.CREATING.toString(), actual);
82 |
83 | when(mockDescription.getStreamStatus()).thenReturn(StreamStatus.ENABLED.toString());
84 | actual = adapter.getStreamStatus();
85 | assertEquals(com.amazonaws.services.kinesis.model.StreamStatus.ACTIVE.toString(), actual);
86 |
87 | when(mockDescription.getStreamStatus()).thenReturn(StreamStatus.DISABLING.toString());
88 | actual = adapter.getStreamStatus();
89 | assertEquals(com.amazonaws.services.kinesis.model.StreamStatus.ACTIVE.toString(), actual);
90 |
91 | when(mockDescription.getStreamStatus()).thenReturn(StreamStatus.DISABLED.toString());
92 | actual = adapter.getStreamStatus();
93 | assertEquals(com.amazonaws.services.kinesis.model.StreamStatus.ACTIVE.toString(), actual);
94 | }
95 |
96 | @Test(expected = UnsupportedOperationException.class)
97 | public void testUnsupportedStreamStatus() {
98 | when(mockDescription.getStreamStatus()).thenReturn(TEST_STRING);
99 | String actual = adapter.getStreamStatus();
100 | }
101 |
102 | @Test(expected = UnsupportedOperationException.class)
103 | public void testSetStreamStatusFailure() {
104 | adapter.setStreamStatus(TEST_STRING);
105 | }
106 |
107 | @Test(expected = UnsupportedOperationException.class)
108 | public void testSetStreamStatusAsType() {
109 | adapter.setStreamStatus(com.amazonaws.services.kinesis.model.StreamStatus.CREATING);
110 | }
111 |
112 | @Test(expected = UnsupportedOperationException.class)
113 | public void testWithStreamStatusFailure() {
114 | adapter.withStreamStatus(TEST_STRING);
115 | }
116 |
117 | @Test(expected = UnsupportedOperationException.class)
118 | public void testWithStreamStatusAsType() {
119 | adapter.withStreamStatus(com.amazonaws.services.kinesis.model.StreamStatus.ACTIVE);
120 | }
121 |
122 | @Test
123 | public void testGetShardsWithItem() {
124 | java.util.List shardList = adapter.getShards();
125 | assertEquals(1, shardList.size());
126 | assertTrue(shardList.get(0) instanceof ShardAdapter);
127 | }
128 |
129 | @Test
130 | public void testGetShardsWithNoItems() {
131 | when(mockDescription.getShards()).thenReturn(new java.util.ArrayList());
132 | StreamDescriptionAdapter localAdapter = new StreamDescriptionAdapter(mockDescription);
133 | java.util.List shardList = localAdapter.getShards();
134 | assertTrue(shardList.isEmpty());
135 | }
136 |
137 | @Test(expected = UnsupportedOperationException.class)
138 | public void testSetShards() {
139 | adapter.setShards(null);
140 | }
141 |
142 | @Test(expected = UnsupportedOperationException.class)
143 | public void testWithShards() {
144 | adapter.withShards(null, null);
145 | }
146 |
147 | @Test(expected = UnsupportedOperationException.class)
148 | public void testWithShards2() {
149 | final Collection shards = Collections.emptyList();
150 | adapter.withShards(shards);
151 | }
152 |
153 | @Test
154 | public void testIsHasMoreShardsTrue() {
155 | when(mockDescription.getLastEvaluatedShardId()).thenReturn(TEST_STRING);
156 | assertTrue(adapter.isHasMoreShards());
157 | }
158 |
159 | @Test
160 | public void testIsHasMoreShardsFalse() {
161 | when(mockDescription.getLastEvaluatedShardId()).thenReturn(null);
162 | assertFalse(adapter.isHasMoreShards());
163 | }
164 |
165 | @Test
166 | public void testGetHasMoreShardsTrue() {
167 | when(mockDescription.getLastEvaluatedShardId()).thenReturn(TEST_STRING);
168 | assertTrue(adapter.getHasMoreShards());
169 | }
170 |
171 | @Test
172 | public void testGetHasMoreShardsFalse() {
173 | when(mockDescription.getLastEvaluatedShardId()).thenReturn(null);
174 | assertFalse(adapter.getHasMoreShards());
175 | }
176 |
177 | @Test(expected = UnsupportedOperationException.class)
178 | public void testSetHasMoreShards() {
179 | adapter.setHasMoreShards(false);
180 | }
181 |
182 | @Test(expected = UnsupportedOperationException.class)
183 | public void testWithHasMoreShards() {
184 | adapter.withHasMoreShards(true);
185 | }
186 |
187 | @Test
188 | public void testRealDataNoShards() {
189 | StreamDescription stream = createStreamDescription(false);
190 | StreamDescriptionAdapter streamAdapter = new StreamDescriptionAdapter(stream);
191 | assertEquals(stream.getStreamArn(), streamAdapter.getStreamName());
192 | assertEquals(stream.getStreamArn(), streamAdapter.getStreamARN());
193 | assertEquals(stream.getShards().size(), streamAdapter.getShards().size());
194 | }
195 |
196 | @Test
197 | public void testRealDataWithShards() {
198 | StreamDescription stream = createStreamDescription(true);
199 | StreamDescriptionAdapter streamAdapter = new StreamDescriptionAdapter(stream);
200 | assertEquals(stream.getStreamArn(), streamAdapter.getStreamName());
201 | assertEquals(stream.getStreamArn(), streamAdapter.getStreamARN());
202 | assertEquals(stream.getShards().size(), streamAdapter.getShards().size());
203 | }
204 |
205 | @Test
206 | public void testGetInternalObject() {
207 | com.amazonaws.services.kinesis.model.StreamDescription kinesisStreamDescription = new StreamDescriptionAdapter(mockDescription);
208 | StreamDescription internalObject = ((StreamDescriptionAdapter) kinesisStreamDescription).getInternalObject();
209 | assertSame(mockDescription, internalObject);
210 | }
211 |
212 | private StreamDescription createStreamDescription(Boolean withShards) {
213 | java.util.List shards = new java.util.ArrayList();
214 | if (withShards) {
215 | shards.add(new Shard());
216 | }
217 | return new StreamDescription().withStreamArn(TEST_STRING).withShards(shards);
218 | }
219 |
220 | }
221 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/CountingRecordProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import org.apache.commons.logging.Log;
9 | import org.apache.commons.logging.LogFactory;
10 |
11 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
12 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShutdownReason;
13 | import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
14 | import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
15 | import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
16 | import com.amazonaws.services.kinesis.model.Record;
17 |
18 | public class CountingRecordProcessor implements IRecordProcessor {
19 |
20 | private static final Log LOG = LogFactory.getLog(CountingRecordProcessor.class);
21 |
22 | private RecordProcessorTracker tracker;
23 |
24 | private String shardId;
25 | private Integer checkpointCounter;
26 | private Integer recordCounter;
27 |
28 | CountingRecordProcessor(RecordProcessorTracker tracker) {
29 | this.tracker = tracker;
30 | }
31 |
32 | @Override
33 | public void initialize(InitializationInput initializationInput) {
34 | this.shardId = initializationInput.getShardId();
35 | checkpointCounter = 0;
36 | recordCounter = 0;
37 | }
38 |
39 | @Override
40 | public void processRecords(ProcessRecordsInput processRecordsInput) {
41 | for (Record record : processRecordsInput.getRecords()) {
42 | recordCounter += 1;
43 | checkpointCounter += 1;
44 | if (checkpointCounter % 10 == 0) {
45 | try {
46 | processRecordsInput.getCheckpointer().checkpoint(record.getSequenceNumber());
47 | } catch (Exception e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | }
52 | }
53 |
54 | @Override
55 | public void shutdown(ShutdownInput shutdownInput) {
56 | if (shutdownInput.getShutdownReason() == ShutdownReason.TERMINATE) {
57 | try {
58 | shutdownInput.getCheckpointer().checkpoint();
59 | } catch (Exception e) {
60 | e.printStackTrace();
61 | }
62 | }
63 | LOG.info("Processed " + recordCounter + " records for " + shardId);
64 | tracker.shardProcessed(shardId, recordCounter);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/ExceptionThrowingLeaseManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 |
11 | import org.apache.commons.logging.Log;
12 | import org.apache.commons.logging.LogFactory;
13 |
14 | import com.amazonaws.services.kinesis.leases.exceptions.DependencyException;
15 | import com.amazonaws.services.kinesis.leases.exceptions.InvalidStateException;
16 | import com.amazonaws.services.kinesis.leases.exceptions.ProvisionedThroughputException;
17 | import com.amazonaws.services.kinesis.leases.impl.KinesisClientLease;
18 | import com.amazonaws.services.kinesis.leases.interfaces.ILeaseManager;
19 |
20 | /**
21 | * Mock Lease Manager by randomly throwing Leasing Exceptions.
22 | * Copied as-is from Kinesis Client Library.
23 | *
24 | */
25 | public class ExceptionThrowingLeaseManager implements ILeaseManager {
26 | private static final Log LOG = LogFactory.getLog(ExceptionThrowingLeaseManager.class);
27 | private static final Throwable EXCEPTION_MSG = new Throwable("Test Exception");
28 |
29 | // Use array below to control in what situations we want to throw exceptions.
30 | private int[] leaseManagerMethodCallingCount;
31 |
32 | /**
33 | * Methods which we support (simulate exceptions).
34 | */
35 | public enum ExceptionThrowingLeaseManagerMethods {
36 | CREATELEASETABLEIFNOTEXISTS(0),
37 | LEASETABLEEXISTS(1),
38 | WAITUNTILLEASETABLEEXISTS(2),
39 | LISTLEASES(3),
40 | CREATELEASEIFNOTEXISTS(4),
41 | GETLEASE(5),
42 | RENEWLEASE(6),
43 | TAKELEASE(7),
44 | EVICTLEASE(8),
45 | DELETELEASE(9),
46 | DELETEALL(10),
47 | UPDATELEASE(11),
48 | NONE(Integer.MIN_VALUE);
49 |
50 | private Integer index;
51 |
52 | ExceptionThrowingLeaseManagerMethods(Integer index) {
53 | this.index = index;
54 | }
55 |
56 | Integer getIndex() {
57 | return this.index;
58 | }
59 | }
60 |
61 | // Define which method should throw exception and when it should throw exception.
62 | private ExceptionThrowingLeaseManagerMethods methodThrowingException = ExceptionThrowingLeaseManagerMethods.NONE;
63 | private int timeThrowingException = Integer.MAX_VALUE;
64 |
65 | // The real local lease manager which would do the real implementations.
66 | private final ILeaseManager leaseManager;
67 |
68 | /**
69 | * Constructor accepts lease manager as only argument.
70 | *
71 | * @param leaseManager which will do the real implementations
72 | */
73 | public ExceptionThrowingLeaseManager(ILeaseManager leaseManager) {
74 | this.leaseManager = leaseManager;
75 | this.leaseManagerMethodCallingCount = new int[ExceptionThrowingLeaseManagerMethods.values().length];
76 | }
77 |
78 | /**
79 | * Set parameters used for throwing exception.
80 | *
81 | * @param method which would throw exception
82 | * @param throwingTime defines what time to throw exception
83 | */
84 | public void setLeaseLeaseManagerThrowingExceptionScenario(ExceptionThrowingLeaseManagerMethods method, int throwingTime) {
85 | this.methodThrowingException = method;
86 | this.timeThrowingException = throwingTime;
87 | }
88 |
89 | /**
90 | * Reset all parameters used for throwing exception.
91 | */
92 | public void clearLeaseManagerThrowingExceptionScenario() {
93 | Arrays.fill(leaseManagerMethodCallingCount, 0);
94 | this.methodThrowingException = ExceptionThrowingLeaseManagerMethods.NONE;
95 | this.timeThrowingException = Integer.MAX_VALUE;
96 | }
97 |
98 | // Throw exception when the conditions are satisfied :
99 | // 1). method equals to methodThrowingException
100 | // 2). method calling count equals to what we want
101 | private void throwExceptions(String methodName, ExceptionThrowingLeaseManagerMethods method)
102 | throws DependencyException {
103 | // Increase calling count for this method
104 | leaseManagerMethodCallingCount[method.getIndex()]++;
105 | if (method.equals(methodThrowingException)
106 | && (leaseManagerMethodCallingCount[method.getIndex()] == timeThrowingException)) {
107 | // Throw Dependency Exception if all conditions are satisfied.
108 | LOG.debug("Throwing DependencyException in " + methodName);
109 | throw new DependencyException(EXCEPTION_MSG);
110 | }
111 | }
112 |
113 | @Override
114 | public boolean createLeaseTableIfNotExists(Long readCapacity, Long writeCapacity)
115 | throws ProvisionedThroughputException, DependencyException {
116 | throwExceptions("createLeaseTableIfNotExists",
117 | ExceptionThrowingLeaseManagerMethods.CREATELEASETABLEIFNOTEXISTS);
118 |
119 | return leaseManager.createLeaseTableIfNotExists(readCapacity, writeCapacity);
120 | }
121 |
122 | @Override
123 | public boolean leaseTableExists() throws DependencyException {
124 | throwExceptions("leaseTableExists", ExceptionThrowingLeaseManagerMethods.LEASETABLEEXISTS);
125 |
126 | return leaseManager.leaseTableExists();
127 | }
128 |
129 | @Override
130 | public boolean waitUntilLeaseTableExists(long secondsBetweenPolls, long timeoutSeconds) throws DependencyException {
131 | throwExceptions("waitUntilLeaseTableExists", ExceptionThrowingLeaseManagerMethods.WAITUNTILLEASETABLEEXISTS);
132 |
133 | return leaseManager.waitUntilLeaseTableExists(secondsBetweenPolls, timeoutSeconds);
134 | }
135 |
136 | @Override
137 | public List listLeases()
138 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
139 | throwExceptions("listLeases", ExceptionThrowingLeaseManagerMethods.LISTLEASES);
140 |
141 | return leaseManager.listLeases();
142 | }
143 |
144 | @Override
145 | public boolean createLeaseIfNotExists(KinesisClientLease lease)
146 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
147 | throwExceptions("createLeaseIfNotExists", ExceptionThrowingLeaseManagerMethods.CREATELEASEIFNOTEXISTS);
148 |
149 | return leaseManager.createLeaseIfNotExists(lease);
150 | }
151 |
152 | @Override
153 | public boolean renewLease(KinesisClientLease lease)
154 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
155 | throwExceptions("renewLease", ExceptionThrowingLeaseManagerMethods.RENEWLEASE);
156 |
157 | return leaseManager.renewLease(lease);
158 | }
159 |
160 | @Override
161 | public boolean takeLease(KinesisClientLease lease, String owner)
162 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
163 | throwExceptions("takeLease", ExceptionThrowingLeaseManagerMethods.TAKELEASE);
164 |
165 | return leaseManager.takeLease(lease, owner);
166 | }
167 |
168 | @Override
169 | public boolean evictLease(KinesisClientLease lease)
170 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
171 | throwExceptions("evictLease", ExceptionThrowingLeaseManagerMethods.EVICTLEASE);
172 |
173 | return leaseManager.evictLease(lease);
174 | }
175 |
176 | @Override
177 | public void deleteLease(KinesisClientLease lease)
178 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
179 | throwExceptions("deleteLease", ExceptionThrowingLeaseManagerMethods.DELETELEASE);
180 |
181 | leaseManager.deleteLease(lease);
182 | }
183 |
184 | @Override
185 | public boolean updateLease(KinesisClientLease lease)
186 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
187 | throwExceptions("updateLease", ExceptionThrowingLeaseManagerMethods.UPDATELEASE);
188 |
189 | return leaseManager.updateLease(lease);
190 | }
191 |
192 | @Override
193 | public KinesisClientLease getLease(String shardId)
194 | throws DependencyException, InvalidStateException, ProvisionedThroughputException {
195 | throwExceptions("getLease", ExceptionThrowingLeaseManagerMethods.GETLEASE);
196 |
197 | return leaseManager.getLease(shardId);
198 | }
199 |
200 | @Override
201 | public void deleteAll() throws DependencyException, InvalidStateException, ProvisionedThroughputException {
202 | throwExceptions("deleteAll", ExceptionThrowingLeaseManagerMethods.DELETEALL);
203 |
204 | leaseManager.deleteAll();
205 | }
206 |
207 | @Override
208 | public boolean isLeaseTableEmpty() throws DependencyException,
209 | InvalidStateException, ProvisionedThroughputException {
210 | return false;
211 | }
212 |
213 | }
214 |
215 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/RecordProcessorTracker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import java.util.HashMap;
9 | import java.util.Map;
10 | import java.util.Set;
11 |
12 | /**
13 | * The RecordProcessorTracker keeps track of the number of records
14 | * per shard which have been successfully processed.
15 | */
16 | public class RecordProcessorTracker {
17 |
18 | private Set shardIds;
19 | private volatile Map processedRecordCounts;
20 |
21 | /**
22 | * Constructor.
23 | *
24 | * @param shardIds The shards which are being processed
25 | */
26 | public RecordProcessorTracker(Set shardIds) {
27 | this.shardIds = shardIds;
28 | processedRecordCounts = new HashMap();
29 | }
30 |
31 | /**
32 | * Invoked by the IRecordProcessor::shutdown() method.
33 | *
34 | * @param shardId The shard ID
35 | * @param count The number of records which have been successfully processed
36 | */
37 | public void shardProcessed(String shardId, Integer count) {
38 | processedRecordCounts.put(shardId, count);
39 | }
40 |
41 | /**
42 | * Determines if the initially specified shards have all been
43 | * completely processed.
44 | *
45 | * @return True if all shards have been processed, false otherwise
46 | */
47 | public boolean isDoneProcessing() {
48 | Set processedShards;
49 | synchronized (processedRecordCounts) {
50 | processedShards = processedRecordCounts.keySet();
51 | }
52 | return processedShards.equals(shardIds);
53 | }
54 |
55 | /**
56 | * Returns the number of successfully processed records for each shard.
57 | *
58 | * @return Number of records processed per shard
59 | */
60 | public Map getProcessedRecordCounts() {
61 | return processedRecordCounts;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/ReplicatingRecordProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import static com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShutdownReason.TERMINATE;
9 |
10 | import java.nio.charset.Charset;
11 |
12 | import org.apache.commons.logging.Log;
13 | import org.apache.commons.logging.LogFactory;
14 |
15 | import com.amazonaws.services.dynamodbv2.streamsadapter.model.RecordAdapter;
16 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
17 | import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
18 | import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
19 | import com.amazonaws.services.kinesis.clientlibrary.types.ShutdownInput;
20 | import com.amazonaws.services.kinesis.model.Record;
21 |
22 | public class ReplicatingRecordProcessor implements IRecordProcessor {
23 |
24 | private static final Log LOG = LogFactory.getLog(ReplicatingRecordProcessor.class);
25 |
26 | private com.amazonaws.services.dynamodbv2.AmazonDynamoDB dynamoDBClient;
27 | private String tableName;
28 | private Integer checkpointCounter = -1;
29 | private Integer processRecordsCallCounter;
30 |
31 | public static final int CHECKPOINT_BATCH_SIZE = 10;
32 |
33 | ReplicatingRecordProcessor(com.amazonaws.services.dynamodbv2.AmazonDynamoDB dynamoDBClient, String tableName) {
34 | this.dynamoDBClient = dynamoDBClient;
35 | this.tableName = tableName;
36 | }
37 |
38 | @Override
39 | public void initialize(InitializationInput initializationInput) {
40 | checkpointCounter = 0;
41 | processRecordsCallCounter = 0;
42 | }
43 |
44 | @Override
45 | public void processRecords(ProcessRecordsInput processRecordsInput) {
46 | processRecordsCallCounter++;
47 | for (Record record : processRecordsInput.getRecords()) {
48 | String data = new String(record.getData().array(), Charset.forName("UTF-8"));
49 | LOG.info("Got record: " + data);
50 | if (record instanceof RecordAdapter) {
51 | com.amazonaws.services.dynamodbv2.model.Record usRecord = ((RecordAdapter) record).getInternalObject();
52 | switch (usRecord.getEventName()) {
53 | case "INSERT":
54 | case "MODIFY":
55 | TestUtil.putItem(dynamoDBClient, tableName, usRecord.getDynamodb().getNewImage());
56 | break;
57 | case "REMOVE":
58 | TestUtil.deleteItem(dynamoDBClient, tableName, usRecord.getDynamodb().getKeys().get("Id").getN());
59 | break;
60 | }
61 | }
62 | checkpointCounter += 1;
63 | if (checkpointCounter % CHECKPOINT_BATCH_SIZE == 0) {
64 | try {
65 | processRecordsInput.getCheckpointer().checkpoint(record.getSequenceNumber());
66 | } catch (Exception e) {
67 | e.printStackTrace();
68 | }
69 | }
70 | }
71 |
72 | }
73 |
74 | @Override
75 | public void shutdown(ShutdownInput shutdownInput) {
76 | if (TERMINATE.equals(shutdownInput.getShutdownReason())) {
77 | try {
78 | shutdownInput.getCheckpointer().checkpoint();
79 | } catch (Exception e) {
80 | e.printStackTrace();
81 | }
82 | }
83 | }
84 |
85 | int getNumRecordsProcessed() {
86 | return checkpointCounter;
87 | }
88 |
89 | int getNumProcessRecordsCalls() {
90 | return processRecordsCallCounter;
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/ShardObjectHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import java.math.BigInteger;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import com.amazonaws.services.kinesis.model.HashKeyRange;
13 | import com.amazonaws.services.kinesis.model.SequenceNumberRange;
14 | import com.amazonaws.services.kinesis.model.Shard;
15 |
16 | /**
17 | * Helper class to create Shard, SequenceRange and related objects.
18 | * Copied as-is from Kinesis Client Library.
19 | */
20 | public class ShardObjectHelper {
21 |
22 | private static final int EXPONENT = 128;
23 |
24 | /**
25 | * Max value of a sequence number (2^128 -1). Useful for defining sequence number range for a shard.
26 | */
27 | static final String MAX_SEQUENCE_NUMBER = new BigInteger("2").pow(EXPONENT).subtract(BigInteger.ONE).toString();
28 |
29 | /**
30 | * Min value of a sequence number (0). Useful for defining sequence number range for a shard.
31 | */
32 | static final String MIN_SEQUENCE_NUMBER = BigInteger.ZERO.toString();
33 |
34 | /**
35 | * Max value of a hash key (2^128 -1). Useful for defining hash key range for a shard.
36 | */
37 | public static final String MAX_HASH_KEY = new BigInteger("2").pow(EXPONENT).subtract(BigInteger.ONE).toString();
38 |
39 | /**
40 | * Min value of a hash key (0). Useful for defining sequence number range for a shard.
41 | */
42 | public static final String MIN_HASH_KEY = BigInteger.ZERO.toString();
43 |
44 | /**
45 | *
46 | */
47 | private ShardObjectHelper() {
48 | }
49 |
50 |
51 | /** Helper method to create a new shard object.
52 | * @param shardId
53 | * @param parentShardId
54 | * @param adjacentParentShardId
55 | * @param sequenceNumberRange
56 | * @return
57 | */
58 | public static Shard newShard(String shardId,
59 | String parentShardId,
60 | String adjacentParentShardId,
61 | SequenceNumberRange sequenceNumberRange) {
62 | return newShard(shardId, parentShardId, adjacentParentShardId, sequenceNumberRange, null);
63 | }
64 |
65 | /** Helper method to create a new shard object.
66 | * @param shardId
67 | * @param parentShardId
68 | * @param adjacentParentShardId
69 | * @param sequenceNumberRange
70 | * @param hashKeyRange
71 | * @return
72 | */
73 | public static Shard newShard(String shardId,
74 | String parentShardId,
75 | String adjacentParentShardId,
76 | SequenceNumberRange sequenceNumberRange,
77 | HashKeyRange hashKeyRange) {
78 | Shard shard = new Shard();
79 | shard.setShardId(shardId);
80 | shard.setParentShardId(parentShardId);
81 | shard.setAdjacentParentShardId(adjacentParentShardId);
82 | shard.setSequenceNumberRange(sequenceNumberRange);
83 | shard.setHashKeyRange(hashKeyRange);
84 |
85 | return shard;
86 | }
87 |
88 | /** Helper method.
89 | * @param startingSequenceNumber
90 | * @param endingSequenceNumber
91 | * @return
92 | */
93 | public static SequenceNumberRange newSequenceNumberRange(String startingSequenceNumber, String endingSequenceNumber) {
94 | SequenceNumberRange range = new SequenceNumberRange();
95 | range.setStartingSequenceNumber(startingSequenceNumber);
96 | range.setEndingSequenceNumber(endingSequenceNumber);
97 | return range;
98 | }
99 |
100 | /** Helper method.
101 | * @param startingHashKey
102 | * @param endingHashKey
103 | * @return
104 | */
105 | public static HashKeyRange newHashKeyRange(String startingHashKey, String endingHashKey) {
106 | HashKeyRange range = new HashKeyRange();
107 | range.setStartingHashKey(startingHashKey);
108 | range.setEndingHashKey(endingHashKey);
109 | return range;
110 | }
111 |
112 | public static List getParentShardIds(Shard shard) {
113 | List parentShardIds = new ArrayList<>(2);
114 | if (shard.getAdjacentParentShardId() != null) {
115 | parentShardIds.add(shard.getAdjacentParentShardId());
116 | }
117 | if (shard.getParentShardId() != null) {
118 | parentShardIds.add(shard.getParentShardId());
119 | }
120 | return parentShardIds;
121 | }
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/TestRecordProcessorFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
9 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
10 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessorFactory;
11 |
12 | /**
13 | * This implementation of IRecordProcessorFactory creates a variety of
14 | * record processors for different testing purposes. The type of processor
15 | * to be created is determined by the constructor.
16 | */
17 | public class TestRecordProcessorFactory implements IRecordProcessorFactory {
18 |
19 | /**
20 | * The types of record processors which can be created by this factory.
21 | */
22 | private enum Processor {
23 | REPLICATING, COUNTING
24 | }
25 |
26 |
27 | private Processor requestedProcessor;
28 | private RecordProcessorTracker tracker;
29 | private IRecordProcessor createdProcessor = null;
30 |
31 | /**
32 | * Using this constructor will result in the createProcessor method
33 | * returning a CountingRecordProcessor.
34 | *
35 | * @param tracker RecordProcessorTracker to keep track of the number of
36 | * processed records per shard
37 | */
38 | public TestRecordProcessorFactory(RecordProcessorTracker tracker) {
39 | this.tracker = tracker;
40 | requestedProcessor = Processor.COUNTING;
41 | }
42 |
43 | private String tableName;
44 | private com.amazonaws.services.dynamodbv2.AmazonDynamoDB dynamoDB;
45 |
46 | /**
47 | * Using this constructor will result in the createProcessor method
48 | * returning a ReplicatingRecordProcessor.
49 | *
50 | * @param credentials AWS credentials used to access DynamoDB
51 | * @param dynamoDBEndpoint DynamoDB endpoint
52 | * @param serviceName Used to initialize the DynamoDB client
53 | * @param tableName The name of the table used for replication
54 | */
55 | public TestRecordProcessorFactory(com.amazonaws.auth.AWSCredentialsProvider credentials, String dynamoDBEndpoint, String serviceName, String tableName) {
56 | this.tableName = tableName;
57 | requestedProcessor = Processor.REPLICATING;
58 |
59 | this.dynamoDB = new AmazonDynamoDBClient(credentials);
60 | dynamoDB.setEndpoint(dynamoDBEndpoint);
61 | ((AmazonDynamoDBClient) dynamoDB).setServiceNameIntern(serviceName);
62 | }
63 |
64 | /**
65 | * Using this constructor creates a replicating processor for an
66 | * embedded(in-memory) instance of DynamoDB local
67 | *
68 | * @param dynamoDB DynamoDB client for embedded DynamoDB instance
69 | * @param tableName The name of the table used for replication
70 | */
71 | public TestRecordProcessorFactory(com.amazonaws.services.dynamodbv2.AmazonDynamoDB dynamoDB, String tableName) {
72 | this.tableName = tableName;
73 | this.dynamoDB = dynamoDB;
74 | requestedProcessor = Processor.REPLICATING;
75 | }
76 |
77 | @Override
78 | public IRecordProcessor createProcessor() {
79 | switch (requestedProcessor) {
80 | case REPLICATING:
81 | createdProcessor = new ReplicatingRecordProcessor(dynamoDB, tableName);
82 | break;
83 | case COUNTING:
84 | createdProcessor = new CountingRecordProcessor(tracker);
85 | break;
86 | default:
87 | createdProcessor = new CountingRecordProcessor(tracker);
88 | break;
89 | }
90 |
91 | return createdProcessor;
92 | }
93 |
94 | /**
95 | * This method returns -1 under the following conditions:
96 | * 1. createProcessor() has not yet been called
97 | * 2. initialize() method on the ReplicatingRecordProcessor instance has not yet been called
98 | * 3. requestedProcessor is COUNTING
99 | *
100 | * @return number of records processed by processRecords
101 | */
102 | public int getNumRecordsProcessed() {
103 | if (createdProcessor == null)
104 | return -1;
105 | switch (requestedProcessor) {
106 | case REPLICATING:
107 | return ((ReplicatingRecordProcessor) createdProcessor).getNumRecordsProcessed();
108 | default:
109 | return -1;
110 | }
111 | }
112 |
113 | public int getNumProcessRecordsCalls() {
114 | if (createdProcessor == null)
115 | return -1;
116 | switch (requestedProcessor) {
117 | case REPLICATING:
118 | return ((ReplicatingRecordProcessor) createdProcessor).getNumProcessRecordsCalls();
119 | default:
120 | return -1;
121 | }
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/dynamodbv2/streamsadapter/util/TestUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.dynamodbv2.streamsadapter.util;
7 |
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
13 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
14 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsClient;
15 | import com.amazonaws.services.dynamodbv2.model.AttributeAction;
16 | import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
17 | import com.amazonaws.services.dynamodbv2.model.AttributeValue;
18 | import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
19 | import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
20 | import com.amazonaws.services.dynamodbv2.model.CreateTableResult;
21 | import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
22 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamRequest;
23 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamResult;
24 | import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest;
25 | import com.amazonaws.services.dynamodbv2.model.DescribeTableResult;
26 | import com.amazonaws.services.dynamodbv2.model.GetRecordsRequest;
27 | import com.amazonaws.services.dynamodbv2.model.GetRecordsResult;
28 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorRequest;
29 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult;
30 | import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
31 | import com.amazonaws.services.dynamodbv2.model.KeyType;
32 | import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
33 | import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
34 | import com.amazonaws.services.dynamodbv2.model.QueryRequest;
35 | import com.amazonaws.services.dynamodbv2.model.QueryResult;
36 | import com.amazonaws.services.dynamodbv2.model.ResourceInUseException;
37 | import com.amazonaws.services.dynamodbv2.model.ScanRequest;
38 | import com.amazonaws.services.dynamodbv2.model.ScanResult;
39 | import com.amazonaws.services.dynamodbv2.model.ShardIteratorType;
40 | import com.amazonaws.services.dynamodbv2.model.StreamDescription;
41 | import com.amazonaws.services.dynamodbv2.model.StreamSpecification;
42 | import com.amazonaws.services.dynamodbv2.model.StreamViewType;
43 | import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest;
44 | import com.amazonaws.services.dynamodbv2.model.UpdateTableRequest;
45 |
46 | public class TestUtil {
47 |
48 | /**
49 | * @return StreamId
50 | */
51 | public static String createTable(com.amazonaws.services.dynamodbv2.AmazonDynamoDB client, String tableName, Boolean withStream) {
52 | java.util.List attributeDefinitions = new ArrayList();
53 | attributeDefinitions.add(new AttributeDefinition().withAttributeName("Id").withAttributeType("N"));
54 |
55 | java.util.List keySchema = new ArrayList();
56 | keySchema.add(new KeySchemaElement().withAttributeName("Id").withKeyType(KeyType.HASH));
57 |
58 | ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput().withReadCapacityUnits(20L).withWriteCapacityUnits(20L);
59 |
60 | CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName).withAttributeDefinitions(attributeDefinitions).withKeySchema(keySchema)
61 | .withProvisionedThroughput(provisionedThroughput);
62 |
63 | if (withStream) {
64 | StreamSpecification streamSpecification = new StreamSpecification();
65 | streamSpecification.setStreamEnabled(true);
66 | streamSpecification.setStreamViewType(StreamViewType.NEW_IMAGE);
67 | createTableRequest.setStreamSpecification(streamSpecification);
68 | }
69 |
70 | try {
71 | CreateTableResult result = client.createTable(createTableRequest);
72 | return result.getTableDescription().getLatestStreamArn();
73 | } catch (ResourceInUseException e) {
74 | return describeTable(client, tableName).getTable().getLatestStreamArn();
75 | }
76 | }
77 |
78 | public static void waitForTableActive(AmazonDynamoDB client, String tableName) throws IllegalStateException {
79 | Integer retries = 0;
80 | Boolean created = false;
81 | while (!created && retries < 100) {
82 | DescribeTableResult result = describeTable(client, tableName);
83 | created = result.getTable().getTableStatus().equals("ACTIVE");
84 | if (created) {
85 | return;
86 | } else {
87 | retries++;
88 | try {
89 | Thread.sleep(500 + 100 * retries);
90 | } catch (InterruptedException e) {
91 | // do nothing
92 | }
93 | }
94 | }
95 | throw new IllegalStateException("Table not active!");
96 | }
97 |
98 | public static void updateTable(AmazonDynamoDBClient client, String tableName, Long readCapacity, Long writeCapacity) {
99 | ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput().withReadCapacityUnits(readCapacity).withWriteCapacityUnits(writeCapacity);
100 |
101 | UpdateTableRequest updateTableRequest = new UpdateTableRequest().withTableName(tableName).withProvisionedThroughput(provisionedThroughput);
102 |
103 | client.updateTable(updateTableRequest);
104 | }
105 |
106 | public static DescribeTableResult describeTable(AmazonDynamoDB client, String tableName) {
107 | return client.describeTable(new DescribeTableRequest().withTableName(tableName));
108 | }
109 |
110 | public static StreamDescription describeStream(AmazonDynamoDBStreamsClient client, String streamArn, String lastEvaluatedShardId) {
111 | DescribeStreamResult result = client.describeStream(new DescribeStreamRequest().withStreamArn(streamArn).withExclusiveStartShardId(lastEvaluatedShardId));
112 | return result.getStreamDescription();
113 | }
114 |
115 | public static ScanResult scanTable(AmazonDynamoDB client, String tableName) {
116 | return client.scan(new ScanRequest().withTableName(tableName));
117 | }
118 |
119 | public static QueryResult queryTable(AmazonDynamoDB client, String tableName, String partitionKey) {
120 | Map expressionAttributeValues = new HashMap();
121 | expressionAttributeValues.put(":v_id", new AttributeValue().withN(partitionKey));
122 |
123 | QueryRequest queryRequest = new QueryRequest().withTableName(tableName).withKeyConditionExpression("Id = :v_id").withExpressionAttributeValues(expressionAttributeValues);
124 |
125 | return client.query(queryRequest);
126 | }
127 |
128 | public static void putItem(AmazonDynamoDB client, String tableName, String id, String val) {
129 | java.util.Map item = new HashMap();
130 | item.put("Id", new AttributeValue().withN(id));
131 | item.put("attribute-1", new AttributeValue().withS(val));
132 |
133 | PutItemRequest putItemRequest = new PutItemRequest().withTableName(tableName).withItem(item);
134 | client.putItem(putItemRequest);
135 | }
136 |
137 | public static void putItem(AmazonDynamoDB client, String tableName, java.util.Map items) {
138 | PutItemRequest putItemRequest = new PutItemRequest().withTableName(tableName).withItem(items);
139 | client.putItem(putItemRequest);
140 | }
141 |
142 | public static void updateItem(AmazonDynamoDB client, String tableName, String id, String val) {
143 | java.util.Map key = new HashMap();
144 | key.put("Id", new AttributeValue().withN(id));
145 |
146 | Map attributeUpdates = new HashMap();
147 | AttributeValueUpdate update = new AttributeValueUpdate().withAction(AttributeAction.PUT).withValue(new AttributeValue().withS(val));
148 | attributeUpdates.put("attribute-2", update);
149 |
150 | UpdateItemRequest updateItemRequest = new UpdateItemRequest().withTableName(tableName).withKey(key).withAttributeUpdates(attributeUpdates);
151 | client.updateItem(updateItemRequest);
152 | }
153 |
154 | public static void deleteItem(AmazonDynamoDB client, String tableName, String id) {
155 | java.util.Map key = new HashMap();
156 | key.put("Id", new AttributeValue().withN(id));
157 |
158 | DeleteItemRequest deleteItemRequest = new DeleteItemRequest().withTableName(tableName).withKey(key);
159 | client.deleteItem(deleteItemRequest);
160 | }
161 |
162 | public static String getShardIterator(AmazonDynamoDBStreamsClient client, String streamArn, String shardId) {
163 | GetShardIteratorResult result =
164 | client.getShardIterator(new GetShardIteratorRequest().withStreamArn(streamArn).withShardId(shardId).withShardIteratorType(ShardIteratorType.TRIM_HORIZON));
165 | return result.getShardIterator();
166 | }
167 |
168 | public static GetRecordsResult getRecords(AmazonDynamoDBStreamsClient client, String shardIterator) {
169 | GetRecordsResult result = client.getRecords(new GetRecordsRequest().withShardIterator(shardIterator));
170 | return result;
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/test/java/com/amazonaws/services/kinesis/clientlibrary/lib/worker/KinesisClientLibraryRecordDeserializationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 | package com.amazonaws.services.kinesis.clientlibrary.lib.worker;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertNotNull;
10 | import static org.junit.Assert.assertTrue;
11 | import static org.mockito.Matchers.any;
12 | import static org.mockito.Mockito.atMost;
13 | import static org.mockito.Mockito.verify;
14 | import static org.powermock.api.mockito.PowerMockito.mock;
15 | import static org.powermock.api.mockito.PowerMockito.when;
16 |
17 | import java.util.ArrayList;
18 | import java.util.Arrays;
19 | import java.util.Date;
20 | import java.util.List;
21 |
22 | import com.amazonaws.services.dynamodbv2.streamsadapter.DynamoDBStreamsDataFetcher;
23 | import com.amazonaws.services.dynamodbv2.streamsadapter.DynamoDBStreamsShutdownTask;
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.mockito.ArgumentCaptor;
27 | import org.powermock.core.classloader.annotations.PrepareForTest;
28 | import org.powermock.modules.junit4.PowerMockRunner;
29 |
30 | import com.amazonaws.auth.BasicAWSCredentials;
31 | import com.amazonaws.internal.StaticCredentialsProvider;
32 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreams;
33 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamRequest;
34 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamResult;
35 | import com.amazonaws.services.dynamodbv2.model.GetRecordsRequest;
36 | import com.amazonaws.services.dynamodbv2.model.GetRecordsResult;
37 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorRequest;
38 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult;
39 | import com.amazonaws.services.dynamodbv2.model.SequenceNumberRange;
40 | import com.amazonaws.services.dynamodbv2.model.Shard;
41 | import com.amazonaws.services.dynamodbv2.model.StreamDescription;
42 | import com.amazonaws.services.dynamodbv2.model.StreamRecord;
43 | import com.amazonaws.services.dynamodbv2.model.StreamStatus;
44 | import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient;
45 | import com.amazonaws.services.dynamodbv2.streamsadapter.model.RecordAdapter;
46 | import com.amazonaws.services.kinesis.clientlibrary.exceptions.KinesisClientLibException;
47 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.ICheckpoint;
48 | import com.amazonaws.services.kinesis.clientlibrary.interfaces.v2.IRecordProcessor;
49 | import com.amazonaws.services.kinesis.clientlibrary.lib.checkpoint.Checkpoint;
50 | import com.amazonaws.services.kinesis.clientlibrary.proxies.IKinesisProxy;
51 | import com.amazonaws.services.kinesis.clientlibrary.proxies.KinesisProxyFactory;
52 | import com.amazonaws.services.kinesis.clientlibrary.types.ExtendedSequenceNumber;
53 | import com.amazonaws.services.kinesis.clientlibrary.types.InitializationInput;
54 | import com.amazonaws.services.kinesis.clientlibrary.types.ProcessRecordsInput;
55 | import com.amazonaws.services.kinesis.model.Record;
56 |
57 | @PrepareForTest({IKinesisProxy.class, KinesisDataFetcher.class})
58 | @RunWith(PowerMockRunner.class)
59 | public class KinesisClientLibraryRecordDeserializationTests {
60 |
61 | /* Constants for mocking DynamoDB Streams */
62 | private static final String STREAM_NAME = "stream-1";
63 | private static final String SHARD_ID = "shard-000000";
64 | private static final String SEQUENCE_NUMBER_0 = "0000000000000000";
65 | private static final String SHARD_ITERATOR = "iterator-0000000000";
66 | private static final Shard SHARD = new Shard().withShardId(SHARD_ID).withSequenceNumberRange(new SequenceNumberRange().withStartingSequenceNumber(SEQUENCE_NUMBER_0));
67 | private static final StreamDescription STREAM_DESCRIPTION =
68 | new StreamDescription().withCreationRequestDateTime(new Date()).withKeySchema().withShards(SHARD).withStreamArn(STREAM_NAME).withStreamStatus(StreamStatus.ENABLED);
69 | private static final StreamRecord STREAM_RECORD_0 = new StreamRecord().withSequenceNumber(SEQUENCE_NUMBER_0).withApproximateCreationDateTime(new Date());
70 | private static final com.amazonaws.services.dynamodbv2.model.Record RECORD_0 = new com.amazonaws.services.dynamodbv2.model.Record().withDynamodb(STREAM_RECORD_0);
71 | private static final List RECORDS = Arrays.asList(RECORD_0);
72 |
73 | /* Mocking the DynamoDB Streams client, Kinesis Client Library checkpoint interfaces, and Record Processor */
74 | private static final AmazonDynamoDBStreams DYNAMODB_STREAMS = mock(AmazonDynamoDBStreams.class);
75 | private static final ICheckpoint CHECKPOINT = mock(ICheckpoint.class);
76 | private static final RecordProcessorCheckpointer CHECKPOINTER = mock(RecordProcessorCheckpointer.class);
77 | private static final IRecordProcessor RECORD_PROCESSOR = mock(IRecordProcessor.class);
78 |
79 | /* Construct higher level Kinesis Client Library objects from the primitive mocks */
80 | private static final AmazonDynamoDBStreamsAdapterClient ADAPTER_CLIENT = new AmazonDynamoDBStreamsAdapterClient(DYNAMODB_STREAMS);
81 | private static final IKinesisProxy KINESIS_PROXY =
82 | new KinesisProxyFactory(new StaticCredentialsProvider(new BasicAWSCredentials("NotAnAccessKey", "NotASecretKey")), ADAPTER_CLIENT).getProxy(STREAM_NAME);
83 | private static final ShardInfo SHARD_INFO = new ShardInfo(SHARD_ID, "concurrencyToken", new ArrayList(), null /*checkpoint*/);
84 | private static final ExtendedSequenceNumber EXTENDED_SEQUENCE_NUMBER = new ExtendedSequenceNumber(SEQUENCE_NUMBER_0);
85 | private static final DynamoDBStreamsDataFetcher KINESIS_DATA_FETCHER = new DynamoDBStreamsDataFetcher(KINESIS_PROXY, SHARD_INFO);
86 | private static final StreamConfig STREAM_CONFIG =
87 | new StreamConfig(KINESIS_PROXY, 1000/* RecordLimit */, 0l /* IdleTimeMillis */, false /* callProcessRecordsForEmptyList */, false /* validateSequenceNumberBeforeCheckpointing */,
88 | InitialPositionInStreamExtended.newInitialPosition(InitialPositionInStream.TRIM_HORIZON));
89 | private static final int GET_RECORDS_ITEM_LIMIT = 1000;
90 | private static final ExtendedSequenceNumber NULL_EXTENDED_SEQUENCE_NUMBER = null;
91 |
92 | @Test
93 | public void testVerifyKCLProvidesRecordAdapter() throws KinesisClientLibException {
94 | // Setup mocks
95 | when(CHECKPOINT.getCheckpointObject(SHARD_ID)).thenReturn(new Checkpoint(ExtendedSequenceNumber.TRIM_HORIZON, NULL_EXTENDED_SEQUENCE_NUMBER));
96 | when(CHECKPOINTER.getLastCheckpointValue()).thenReturn(ExtendedSequenceNumber.TRIM_HORIZON);
97 | when(DYNAMODB_STREAMS.describeStream(any(DescribeStreamRequest.class))).thenReturn(new DescribeStreamResult().withStreamDescription(STREAM_DESCRIPTION));
98 | when(DYNAMODB_STREAMS.getShardIterator(any(GetShardIteratorRequest.class))).thenReturn(new GetShardIteratorResult().withShardIterator(SHARD_ITERATOR));
99 | when(DYNAMODB_STREAMS.getRecords(any(GetRecordsRequest.class))).thenReturn(new GetRecordsResult().withNextShardIterator(SHARD_ITERATOR).withRecords(RECORDS));
100 |
101 |
102 | GetRecordsCache cache = new BlockingGetRecordsCache(GET_RECORDS_ITEM_LIMIT, new SynchronousGetRecordsRetrievalStrategy(KINESIS_DATA_FETCHER));
103 | // Initialize the Record Processor
104 | InitializeTask initializeTask = new InitializeTask(SHARD_INFO, RECORD_PROCESSOR, CHECKPOINT, CHECKPOINTER, KINESIS_DATA_FETCHER, 0L /* backoffTimeMillis */, STREAM_CONFIG, cache);
105 | initializeTask.call();
106 | // Execute process task
107 | ProcessTask processTask = new ProcessTask(SHARD_INFO, STREAM_CONFIG, RECORD_PROCESSOR, CHECKPOINTER, KINESIS_DATA_FETCHER, 0L /* backoffTimeMillis */, false /*skipShardSyncAtWorkerInitializationIfLeasesExist*/, cache);
108 | processTask.call();
109 |
110 | // Verify mocks
111 | verify(CHECKPOINT).getCheckpointObject(SHARD_ID);
112 | verify(CHECKPOINTER).setLargestPermittedCheckpointValue(EXTENDED_SEQUENCE_NUMBER);
113 | verify(CHECKPOINTER).setInitialCheckpointValue(ExtendedSequenceNumber.TRIM_HORIZON);
114 | verify(CHECKPOINTER).getLastCheckpointValue();
115 | verify(DYNAMODB_STREAMS, atMost(1)).describeStream(any(DescribeStreamRequest.class));
116 | verify(DYNAMODB_STREAMS).getShardIterator(any(GetShardIteratorRequest.class));
117 | verify(DYNAMODB_STREAMS).getRecords(any(GetRecordsRequest.class));
118 | // Capture the input to the ProcessRecords method
119 | final ArgumentCaptor processRecordsInputCapture = ArgumentCaptor.forClass(ProcessRecordsInput.class);
120 | verify(RECORD_PROCESSOR).initialize(any(InitializationInput.class));
121 | verify(RECORD_PROCESSOR).processRecords(processRecordsInputCapture.capture());
122 |
123 | // Verify the Records are delivered to the Record Processor as RecordAdapter objects
124 | ProcessRecordsInput processRecordsInput = processRecordsInputCapture.getValue();
125 | assertNotNull(processRecordsInput);
126 | assertNotNull(processRecordsInput.getRecords());
127 | assertEquals(RECORDS.size(), processRecordsInput.getRecords().size());
128 | for (Record record : processRecordsInput.getRecords()) {
129 | assertTrue("Kinesis Client Library is unwrapping the DynamoDB Streams Record Adapter", record instanceof RecordAdapter);
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------