/* * Copyright 2013-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Amazon Software License (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/asl/ * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.rakam.aws.kinesis; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.kinesis.AmazonKinesisClient; import com.amazonaws.services.kinesis.model.CreateStreamRequest; import com.amazonaws.services.kinesis.model.DeleteStreamRequest; import com.amazonaws.services.kinesis.model.DescribeStreamRequest; import com.amazonaws.services.kinesis.model.ListStreamsRequest; import com.amazonaws.services.kinesis.model.ListStreamsResult; import com.amazonaws.services.kinesis.model.ResourceNotFoundException; import io.airlift.log.Logger; import java.util.List; /** * Utilities to create and delete Amazon Kinesis streams. */ public final class KinesisUtils { private static Logger LOG = Logger.get(KinesisUtils.class); private KinesisUtils() throws InstantiationException{ throw new InstantiationException("The class is not created for instantiation"); } /** * Creates an Amazon Kinesis stream if it does not exist and waits for it to become available * * @param kinesisClient * The {@link com.amazonaws.services.kinesis.AmazonKinesisClient} with Amazon Kinesis read and write privileges * @param streamName * The Amazon Kinesis stream name to create * @param shardCount * The shard count to create the stream with * @throws IllegalStateException * Invalid Amazon Kinesis stream state * @throws IllegalStateException * Stream does not go active before the timeout */ public static void createAndWaitForStreamToBecomeAvailable(AmazonKinesisClient kinesisClient, String streamName, int shardCount) { if (streamExists(kinesisClient, streamName)) { String state = streamState(kinesisClient, streamName); switch (state) { case "DELETING": long startTime = System.currentTimeMillis(); long endTime = startTime + 1000 * 120; while (System.currentTimeMillis() < endTime && streamExists(kinesisClient, streamName)) { try { LOG.info("...Deleting Stream " + streamName + "..."); Thread.sleep(1000 * 10); } catch (InterruptedException e) { } } if (streamExists(kinesisClient, streamName)) { LOG.error("KinesisUtils timed out waiting for stream " + streamName + " to delete"); throw new IllegalStateException("KinesisUtils timed out waiting for stream " + streamName + " to delete"); } case "ACTIVE": LOG.info("Stream " + streamName + " is ACTIVE"); return; case "CREATING": break; case "UPDATING": LOG.info("Stream " + streamName + " is UPDATING"); return; default: throw new IllegalStateException("Illegal stream state: " + state); } } else { CreateStreamRequest createStreamRequest = new CreateStreamRequest(); createStreamRequest.setStreamName(streamName); createStreamRequest.setShardCount(shardCount); kinesisClient.createStream(createStreamRequest); LOG.info("Stream " + streamName + " created"); } long startTime = System.currentTimeMillis(); long endTime = startTime + (10 * 60 * 1000); while (System.currentTimeMillis() < endTime) { try { Thread.sleep(1000 * 10); } catch (Exception e) { } try { String streamStatus = streamState(kinesisClient, streamName); if (streamStatus.equals("ACTIVE")) { LOG.info("Stream " + streamName + " is ACTIVE"); return; } } catch (ResourceNotFoundException e) { throw new IllegalStateException("Stream " + streamName + " never went active"); } } } /** * Helper method to determine if an Amazon Kinesis stream exists. * * @param kinesisClient * The {@link com.amazonaws.services.kinesis.AmazonKinesisClient} with Amazon Kinesis read privileges * @param streamName * The Amazon Kinesis stream to check for * @return true if the Amazon Kinesis stream exists, otherwise return false */ private static boolean streamExists(AmazonKinesisClient kinesisClient, String streamName) { DescribeStreamRequest describeStreamRequest = new DescribeStreamRequest(); describeStreamRequest.setStreamName(streamName); try { kinesisClient.describeStream(describeStreamRequest); return true; } catch (ResourceNotFoundException e) { return false; } } /** * Return the state of a Amazon Kinesis stream. * * @param kinesisClient * The {@link com.amazonaws.services.kinesis.AmazonKinesisClient} with Amazon Kinesis read privileges * @param streamName * The Amazon Kinesis stream to get the state of * @return String representation of the Stream state */ private static String streamState(AmazonKinesisClient kinesisClient, String streamName) { DescribeStreamRequest describeStreamRequest = new DescribeStreamRequest(); describeStreamRequest.setStreamName(streamName); try { return kinesisClient.describeStream(describeStreamRequest).getStreamDescription().getStreamStatus(); } catch (AmazonServiceException e) { return null; } } /** * Gets a list of all Amazon Kinesis streams * * @param kinesisClient * The {@link com.amazonaws.services.kinesis.AmazonKinesisClient} with Amazon Kinesis read privileges * @return list of Amazon Kinesis streams */ public static List<String> listAllStreams(AmazonKinesisClient kinesisClient) { ListStreamsRequest listStreamsRequest = new ListStreamsRequest(); listStreamsRequest.setLimit(10); ListStreamsResult listStreamsResult = kinesisClient.listStreams(listStreamsRequest); List<String> streamNames = listStreamsResult.getStreamNames(); while (listStreamsResult.isHasMoreStreams()) { if (!streamNames.isEmpty()) { listStreamsRequest.setExclusiveStartStreamName(streamNames.get(streamNames.size() - 1)); } listStreamsResult = kinesisClient.listStreams(listStreamsRequest); streamNames.addAll(listStreamsResult.getStreamNames()); } return streamNames; } /** * Deletes an Amazon Kinesis stream if it exists. * * @param kinesisClient * The {@link com.amazonaws.services.kinesis.AmazonKinesisClient} with Amazon Kinesis read and write privileges * @param streamName * The Amazon Kinesis stream to delete */ public static void deleteStream(AmazonKinesisClient kinesisClient, String streamName) { if (streamExists(kinesisClient, streamName)) { DeleteStreamRequest deleteStreamRequest = new DeleteStreamRequest(); deleteStreamRequest.setStreamName(streamName); kinesisClient.deleteStream(deleteStreamRequest); LOG.info("Deleting stream " + streamName); } else { LOG.warn("Stream " + streamName + " does not exist"); } } }