public List<CloudtrailSNSNotification> parse(Message message) {

        LOG.debug("Parsing message.");
        try {
            LOG.debug("Reading message body {}.", message.getBody());
            final SQSMessage envelope = objectMapper.readValue(message.getBody(), SQSMessage.class);

            if (envelope.message == null) {
                LOG.warn("Message is empty. Processing of message has been aborted. Verify that the SQS subscription in AWS is NOT set to send raw data.");
                return Collections.emptyList();
            }

            LOG.debug("Reading message envelope {}.", envelope.message);
            if (envelope.message.contains(CLOUD_TRAIL_VALIDATION_MESSAGE)) {
                return Collections.emptyList();
            }

            final CloudtrailWriteNotification notification = objectMapper.readValue(envelope.message, CloudtrailWriteNotification.class);

            final List<String> s3ObjectKeys = notification.s3ObjectKey;
            if (s3ObjectKeys == null) {
                LOG.debug("No S3 object keys parsed.");
                return Collections.emptyList();
            }

            LOG.debug("Processing [{}] S3 keys.", s3ObjectKeys.size());
            final List<CloudtrailSNSNotification> notifications = new ArrayList<>(s3ObjectKeys.size());
            for (String s3ObjectKey : s3ObjectKeys) {
                notifications.add(new CloudtrailSNSNotification(message.getReceiptHandle(), notification.s3Bucket, s3ObjectKey));
            }

            LOG.debug("Returning [{}] notifications.", notifications.size());
            return notifications;
        } catch (IOException e) {
            LOG.error("Parsing exception.", e);
            /* Don't throw an exception that would halt processing for one parsing failure.
             * Sometimes occasional non-JSON test messages will come through. If this happens,
             * just log the error and keep processing.
              *
              * Returning an empty list here is OK and should be caught by the caller. */
            return new ArrayList<>();
        }
    }
@Test
    public void issue_44() throws Exception {
        // https://github.com/Graylog2/graylog-plugin-aws/issues/44
        final Message message = new Message()
                .withBody("{\n" +
                        "  \"Type\" : \"Notification\",\n" +
                        "  \"MessageId\" : \"5b0a73e6-a4f8-11e7-8dfb-8f76310a10a8\",\n" +
                        "  \"TopicArn\" : \"arn:aws:sns:eu-west-1:123456789012:cloudtrail-log-write\",\n" +
                        "  \"Subject\" : \"[AWS Config:eu-west-1] AWS::RDS::DBSnapshot rds:instance-2017-09-03-23-11 Dele...\",\n" +
                        "  \"Message\" : \"{\\\"configurationItemDiff\\\":{\\\"changedProperties\\\":{\\\"Relationships.0\\\":{\\\"previousValue\\\":{\\\"resourceId\\\":\\\"vpc-12345678\\\",\\\"resourceName\\\":null,\\\"resourceType\\\":\\\"AWS::EC2::VPC\\\",\\\"name\\\":\\\"Is associated with Vpc\\\"},\\\"updatedValue\\\":null,\\\"changeType\\\":\\\"DELETE\\\"},\\\"SupplementaryConfiguration.Tags\\\":{\\\"previousValue\\\":[],\\\"updatedValue\\\":null,\\\"changeType\\\":\\\"DELETE\\\"},\\\"SupplementaryConfiguration.DBSnapshotAttributes\\\":{\\\"previousValue\\\":[{\\\"attributeName\\\":\\\"restore\\\",\\\"attributeValues\\\":[]}],\\\"updatedValue\\\":null,\\\"changeType\\\":\\\"DELETE\\\"},\\\"Configuration\\\":{\\\"previousValue\\\":{\\\"dBSnapshotIdentifier\\\":\\\"rds:instance-2017-09-03-23-11\\\",\\\"dBInstanceIdentifier\\\":\\\"instance\\\",\\\"snapshotCreateTime\\\":\\\"2017-09-03T23:11:38.218Z\\\",\\\"engine\\\":\\\"mysql\\\",\\\"allocatedStorage\\\":200,\\\"status\\\":\\\"available\\\",\\\"port\\\":3306,\\\"availabilityZone\\\":\\\"eu-west-1b\\\",\\\"vpcId\\\":\\\"vpc-12345678\\\",\\\"instanceCreateTime\\\":\\\"2015-04-09T07:08:07.476Z\\\",\\\"masterUsername\\\":\\\"root\\\",\\\"engineVersion\\\":\\\"5.6.34\\\",\\\"licenseModel\\\":\\\"general-public-license\\\",\\\"snapshotType\\\":\\\"automated\\\",\\\"iops\\\":null,\\\"optionGroupName\\\":\\\"default:mysql-5-6\\\",\\\"percentProgress\\\":100,\\\"sourceRegion\\\":null,\\\"sourceDBSnapshotIdentifier\\\":null,\\\"storageType\\\":\\\"standard\\\",\\\"tdeCredentialArn\\\":null,\\\"encrypted\\\":false,\\\"kmsKeyId\\\":null,\\\"dBSnapshotArn\\\":\\\"arn:aws:rds:eu-west-1:123456789012:snapshot:rds:instance-2017-09-03-23-11\\\",\\\"timezone\\\":null,\\\"iAMDatabaseAuthenticationEnabled\\\":false},\\\"updatedValue\\\":null,\\\"changeType\\\":\\\"DELETE\\\"}},\\\"changeType\\\":\\\"DELETE\\\"},\\\"configurationItem\\\":{\\\"relatedEvents\\\":[],\\\"relationships\\\":[],\\\"configuration\\\":null,\\\"supplementaryConfiguration\\\":{},\\\"tags\\\":{},\\\"configurationItemVersion\\\":\\\"1.2\\\",\\\"configurationItemCaptureTime\\\":\\\"2017-09-28T19:54:47.815Z\\\",\\\"configurationStateId\\\":1234567890123,\\\"awsAccountId\\\":\\\"123456789012\\\",\\\"configurationItemStatus\\\":\\\"ResourceDeleted\\\",\\\"resourceType\\\":\\\"AWS::RDS::DBSnapshot\\\",\\\"resourceId\\\":\\\"rds:instance-2017-09-03-23-11\\\",\\\"resourceName\\\":\\\"rds:instance-2017-09-03-23-11\\\",\\\"ARN\\\":\\\"arn:aws:rds:eu-west-1:123456789012:snapshot:rds:instance-2017-09-03-23-11\\\",\\\"awsRegion\\\":\\\"eu-west-1\\\",\\\"availabilityZone\\\":null,\\\"configurationStateMd5Hash\\\":\\\"b026324c6904b2a9cb4b88d6d61c81d1\\\",\\\"resourceCreationTime\\\":null},\\\"notificationCreationTime\\\":\\\"2017-09-28T19:54:48.311Z\\\",\\\"messageType\\\":\\\"ConfigurationItemChangeNotification\\\",\\\"recordVersion\\\":\\\"1.2\\\"}\",\n" +
                        "  \"Timestamp\" : \"2017-09-28T19:54:58.543Z\",\n" +
                        "  \"SignatureVersion\" : \"1\",\n" +
                        "  \"Signature\" : \"...\",\n" +
                        "  \"SigningCertURL\" : \"https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-....pem\",\n" +
                        "  \"UnsubscribeURL\" : \"https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:123456789012:cloudtrail-log-write:5b0a73e6-a4f8-11e7-8dfb-8f76310a10a8\"\n" +
                        "}");

        CloudtrailSNSNotificationParser parser = new CloudtrailSNSNotificationParser(objectMapper);

        List<CloudtrailSNSNotification> notifications = parser.parse(message);
        assertTrue(notifications.isEmpty());
    }