All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.dynamodbv2.streamsadapter.model.AmazonServiceExceptionTransformer Maven / Gradle / Ivy

/*
 * Copyright 2014-2015 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 com.amazonaws.services.dynamodbv2.streamsadapter.model;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.dynamodbv2.model.InternalServerErrorException;
import com.amazonaws.services.dynamodbv2.streamsadapter.AmazonDynamoDBStreamsAdapterClient.SkipRecordsBehavior;
import com.amazonaws.services.dynamodbv2.streamsadapter.exceptions.UnableToReadMoreRecordsException;

/**
 * 

* This class transforms an Amazon DynamoDB Streams AmazonServiceException into a compatible Amazon Kinesis * AmazonServiceException. *

* Applicable API calls: * * */ public class AmazonServiceExceptionTransformer { private static final String TRIMMED_DATA_KCL_RETRY_MESSAGE = "Attempted to get a shard iterator for a trimmed shard. Data has been lost"; /** * Error message used for ThrottlingException by the Amazon DynamoDB Streams service. */ public static final String DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE = "ThrottlingException"; /** * Empty String used when an Exception requires an error message, but none is supplied. */ public static final String EMPTY_STRING = ""; /** * Error message used for InternalFailure by the Amazon Kinesis service. */ public static final String KINESIS_INTERNAL_ERROR_MESSAGE = "InternalFailure"; /** * Error message used for ValidationError by the Amazon Kinesis service. */ public static final String KINESIS_VALIDATION_ERROR_MESSAGE = "ValidationError"; private static final Log LOG = LogFactory.getLog(AmazonServiceExceptionTransformer.class); /** * After determining the transformation in the API specific transform method, this helper method sets the * transformed exception's fields to match the original exception. If no transformation occurred, the original * exception is returned. * * @param original * The original DynamoDB Streams exception * @param transformed * The equivalent Kinesis exception that needs its fields updated * @return The final result of the transformation ready to be thrown by the adapter */ private static AmazonServiceException applyFields(AmazonServiceException original, AmazonServiceException transformed) { if (transformed == null) { LOG.error("Could not transform a DynamoDB AmazonServiceException to a compatible Kinesis exception", original); return original; } // Here we update the transformed exception fields with the original exception values if (original.getErrorCode() != null) { transformed.setErrorCode(original.getErrorCode()); } // Null is transformed to UNKNOWN, so a null value is impossible. transformed.setErrorType(original.getErrorType()); if (original.getRequestId() != null) { transformed.setRequestId(original.getRequestId()); } if (original.getServiceName() != null) { transformed.setServiceName(original.getServiceName()); } transformed.setStatusCode(original.getStatusCode()); LOG.error( String.format("DynamoDB Streams exception: %s tranformed to Kinesis %s", original.getClass(), transformed.getClass()), original); return transformed; } /** * Builds the error message for a transformed exception. Returns the original error message or an empty String if * the original error message was null. * * @param ase * The original exception * @return The error message for a transformed exception. Returns the original error message or an empty String if * the original error message was null. */ private static String buildErrorMessage(AmazonServiceException ase) { if (ase.getErrorMessage() == null) { return EMPTY_STRING; } return ase.getErrorMessage(); } /** * Transforms Amazon DynamoDB Streams exceptions to compatible Amazon Kinesis exceptions for the DescribeStream API. * * The following transformations are applied:
* (1) InternalServerError
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.InternalServerErrorException}
* Amazon Kinesis: {@link com.amazonaws.AmazonServiceException}
* Notes: SDK relies on the 500 series StatusCode to identify that the issue was service side
*
* (2) ResourceNotFound
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException}
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ResourceNotFoundException}
* Notes: N/A
*
* (2) ThrottlingException
* Amazon DynamoDB Streams: {@link com.amazonaws.AmazonServiceException} with ErrorCode * {@value com.amazonaws.services.dynamodbv2.streamsadapter.model.AmazonServiceExceptionTransformer#DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE} *
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.LimitExceededException}
* Notes: N/A
* * @param ase * The Amazon DynamoDB Streams exception thrown by a DescribeStream call * @return A compatible Amazon Kinesis DescribeStream exception */ public static AmazonServiceException transformDynamoDBStreamsToKinesisDescribeStream(AmazonServiceException ase) { final AmazonServiceException transformed; if (ase == null) { return ase; } if (ase instanceof InternalServerErrorException) { // (1) transformed = new AmazonServiceException(buildErrorMessage(ase), ase); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException) { // (2) transformed = new com.amazonaws.services.kinesis.model.ResourceNotFoundException(buildErrorMessage(ase)); } else if (DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE.equals(ase.getErrorCode())) { // (3) transformed = new com.amazonaws.services.kinesis.model.LimitExceededException(buildErrorMessage(ase)); } else { transformed = null; } return applyFields(ase, transformed); } /** * Transforms Amazon DynamoDB Streams exceptions to compatible Amazon Kinesis exceptions for the GetRecords API. * * The following transformations are applied:
* (1) ExpiredIterator
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.ExpiredIteratorException}
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ExpiredIteratorException}
* Notes: N/A
*
* (2) InternalServerError
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.InternalServerErrorException}
* Amazon Kinesis: {@link com.amazonaws.AmazonServiceException}
* Notes: SDK relies on the 500 series StatusCode to identify that the issue was service side
*
* (3) LimitExceeded
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.LimitExceededException}
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException}
* Notes: N/A
*
* (4) ResourceNotFound
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException}
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ResourceNotFoundException}
* Notes: N/A
*
* (5) ThrottlingException
* Amazon DynamoDB Streams: {@link com.amazonaws.AmazonServiceException} with ErrorCode * {@value com.amazonaws.services.dynamodbv2.streamsadapter.model.AmazonServiceExceptionTransformer#DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE} *
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException}
* Notes: N/A
* (6) TrimmedDataAccess
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.TrimmedDataAccessException}
* Amazon Kinesis: N/A
* Notes: FIXME Amazon Kinesis does not communicate trimmed data; the service retrieves the oldest available records * for the shard and returns those as if no trimming occurred. Because no context information about the shardId or * streamId is available in the GetRecords request, the adapter relies on the user attempting to get a fresh shard * iterator using the GetShardIterator API when an ExpiredIteratorException is thrown. If data loss is unacceptable, * an {@link UnableToReadMoreRecordsException} is thrown.
* * @param ase * The Amazon DynamoDB Streams exception thrown by a GetRecords call * @param skipRecordsBehavior * The {@link SkipRecordsBehavior} for the adapter * @return A compatible Amazon Kinesis GetRecords exception */ public static AmazonServiceException transformDynamoDBStreamsToKinesisGetRecords(AmazonServiceException ase, SkipRecordsBehavior skipRecordsBehavior) { final AmazonServiceException transformed; if (ase == null) { return ase; } if (ase instanceof com.amazonaws.services.dynamodbv2.model.ExpiredIteratorException) { // (1) transformed = new com.amazonaws.services.kinesis.model.ExpiredIteratorException(buildErrorMessage(ase)); } else if (ase instanceof InternalServerErrorException) { // (2) transformed = new AmazonServiceException(buildErrorMessage(ase), ase); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.LimitExceededException) { // (3) transformed = new com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException( buildErrorMessage(ase)); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException) { // (4) transformed = new com.amazonaws.services.kinesis.model.ResourceNotFoundException(buildErrorMessage(ase)); } else if (DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE.equals(ase.getErrorCode())) { // (5) transformed = new com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException( buildErrorMessage(ase)); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.TrimmedDataAccessException) { // (6) if (skipRecordsBehavior == SkipRecordsBehavior.SKIP_RECORDS_TO_TRIM_HORIZON) { // Data loss is acceptable transformed = new com.amazonaws.services.kinesis.model.ExpiredIteratorException(buildErrorMessage(ase)); } else { // Data loss is NOT acceptable throw new UnableToReadMoreRecordsException("Attempted to access trimmed data. Data has been lost", ase); } } else { transformed = null; } return applyFields(ase, transformed); } /** * Transforms Amazon DynamoDB Streams exceptions to compatible Amazon Kinesis exceptions for the GetShardIterator * API. * * The following transformations are applied:
* (1) InternalServerError
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.InternalServerErrorException}
* Amazon Kinesis: {@link com.amazonaws.AmazonServiceException}
* Notes: SDK relies on the 500 series StatusCode to identify that the issue was service side
*
* (2) ResourceNotFound
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException}
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ResourceNotFoundException}
* Notes: Amazon Kinesis does not differentiate TrimmedData and ResourceNotFound. In the case that data loss is not * acceptable, the adapter throws an {@link UnableToReadMoreRecordsException}
*
* (3) ThrottlingException
* Amazon DynamoDB Streams: {@link com.amazonaws.AmazonServiceException} with ErrorCode * {@value com.amazonaws.services.dynamodbv2.streamsadapter.model.AmazonServiceExceptionTransformer#DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE} *
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException}
* Notes: N/A
* (4) TrimmedDataAccess
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.TrimmedDataAccessException}
* Amazon Kinesis: N/A
* Notes: FIXME Amazon Kinesis does not communicate trimmed data; the service returns a valid shard iterator for the * oldest available records for the specified shard as if no trimming occurred. If data loss is acceptable, the * adapter mimics this behavior by intercepting the exception and retrieving a shard iterator for TRIM_HORIZON. If * data loss is unacceptable, an {@link UnableToReadMoreRecordsException} is thrown.
* * @param ase * The Amazon DynamoDB Streams exception thrown by a GetRecords call * @param skipRecordsBehavior * The {@link SkipRecordsBehavior} for the adapter * @return A compatible Amazon Kinesis GetRecords exception */ public static AmazonServiceException transformDynamoDBStreamsToKinesisGetShardIterator(AmazonServiceException ase, SkipRecordsBehavior skipRecordsBehavior) { final AmazonServiceException transformed; if (ase == null) { return ase; } if (ase instanceof InternalServerErrorException) { // (1) transformed = new AmazonServiceException(buildErrorMessage(ase), ase); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException) { // (2) if (skipRecordsBehavior == SkipRecordsBehavior.SKIP_RECORDS_TO_TRIM_HORIZON) { transformed = new com.amazonaws.services.kinesis.model.ResourceNotFoundException(buildErrorMessage(ase)); } else { throw new UnableToReadMoreRecordsException(TRIMMED_DATA_KCL_RETRY_MESSAGE, ase); } } else if (DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE.equals(ase.getErrorCode())) { // (3) transformed = new com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException( buildErrorMessage(ase)); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.TrimmedDataAccessException) { // (4) if (skipRecordsBehavior == SkipRecordsBehavior.SKIP_RECORDS_TO_TRIM_HORIZON) { transformed = new com.amazonaws.services.kinesis.model.ResourceNotFoundException(buildErrorMessage(ase)); } else { throw new UnableToReadMoreRecordsException(TRIMMED_DATA_KCL_RETRY_MESSAGE, ase); } } else { transformed = null; } return applyFields(ase, transformed); } /** * Transforms Amazon DynamoDB Streams exceptions to compatible Amazon Kinesis exceptions for the ListStreams API. * * The following transformations are applied:
* (1) InternalServerError
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.InternalServerErrorException}
* Amazon Kinesis: {@link com.amazonaws.AmazonServiceException}
* Notes: SDK relies on the 500 series StatusCode to identify that the issue was service side
*
* (2) ResourceNotFound
* Amazon DynamoDB Streams: {@link com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException}
* Amazon Kinesis: N/A
* Notes: A compatible transformation is to an AmazonServiceException with a 400 series StatusCode
*
* (3) ThrottlingException
* Amazon DynamoDB Streams: {@link com.amazonaws.AmazonServiceException} with ErrorCode * {@value com.amazonaws.services.dynamodbv2.streamsadapter.model.AmazonServiceExceptionTransformer#DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE} *
* Amazon Kinesis: {@link com.amazonaws.services.kinesis.model.LimitExceededException}
* Notes: N/A
* * @param ase * The Amazon DynamoDB Streams exception thrown by a ListStreams call * @return A compatible Amazon Kinesis ListStreams exception */ public static AmazonServiceException transformDynamoDBStreamsToKinesisListStreams(AmazonServiceException ase) { final AmazonServiceException transformed; if (ase == null) { return ase; } if (ase instanceof InternalServerErrorException) { // (1) transformed = new AmazonServiceException(buildErrorMessage(ase), ase); } else if (ase instanceof com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException) { // (2) transformed = new AmazonServiceException(buildErrorMessage(ase), ase); } else if (DYNAMODB_STREAMS_THROTTLING_EXCEPTION_ERROR_CODE.equals(ase.getErrorCode())) { // (3) transformed = new com.amazonaws.services.kinesis.model.LimitExceededException(buildErrorMessage(ase)); } else { transformed = null; } return applyFields(ase, transformed); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy