/*******************************************************************************
* Copyright 2010-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (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/apache2.0
*
* 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.cloudtrail.processinglibrary.utils;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.cloudtrail.processinglibrary.exceptions.ProcessingLibraryException;
import com.amazonaws.services.cloudtrail.processinglibrary.interfaces.ExceptionHandler;
import com.amazonaws.services.cloudtrail.processinglibrary.interfaces.ProgressReporter;
import com.amazonaws.services.cloudtrail.processinglibrary.model.SourceAttributeKeys;
import com.amazonaws.services.cloudtrail.processinglibrary.progress.ProgressStatus;
import com.amazonaws.services.sqs.model.Message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
/**
* Utility methods used by the AWS CloudTrail Processing Library.
*/
public class LibraryUtils {
private static final String UNDER_SCORE = "_";
private static final String FORWARD_SLASH = "/";
private static final String AMAZONAWS_COM = ".amazonaws.com/";
private static final String UTC_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String UTC_TIME_ZONE = "UTC";
/**
* Check that an object is not null
; throw an exception if it
* is.
*
* @param argument the Object to check.
* @param message a description string that will be sent with the exception.
* @throws IllegalStateException if the passed-in object is null
.
*/
public static void checkArgumentNotNull(Object argument, String message) {
if (argument == null) {
throw new IllegalStateException(message);
}
}
/**
* Check a conditional value or expression, if true
, throw an
* exception.
*
* @param condition a boolean value or an expression to check.
* @param message a description string that will be sent with the exception.
* @throws IllegalStateException if the condition expression is true
.
*/
public static void checkCondition(boolean condition, String message) {
if (condition) {
throw new IllegalStateException(message);
}
}
/**
* Convert an
* InputSteam to a byte array.
*
* @param inputStream the InputStream
to convert.
* @return a byte array containing the data from the input stream.
* @throws IOException if the InputStream
could not be converted.
*/
public static byte[] toByteArray(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] bytes = new byte[1024];
while ((nRead = inputStream.read(bytes, 0, 1024)) != -1) {
buffer.write(bytes, 0, nRead);
}
return buffer.toByteArray();
}
/**
* Split an HTTP representation of an Amazon S3 URL to bucket name and object key.
*
* For example: *
* input: s3ObjectHttpUrl = http://s3-us-west-2.amazonaws.com/mybucket/myobjectpath1/myobjectpath2/myobject.extension * output: {"mybucket", "myobjectpath1/myobjectpath2/myobject.extension"} ** * @param s3ObjectHttpUrl the URL of the S3 object to split. * @return a two-element string array: the first element is the bucket name, * and the second element is the object key. */ public static String[] toBucketNameObjectKey(String s3ObjectHttpUrl) { if (s3ObjectHttpUrl == null) { return null; } int start = s3ObjectHttpUrl.indexOf(AMAZONAWS_COM); int length = s3ObjectHttpUrl.length(); if (start != -1) { String bucketNameAndObjectKey = s3ObjectHttpUrl.substring(start + AMAZONAWS_COM.length(), length); return bucketNameAndObjectKey.split(FORWARD_SLASH, 2); } return null; } /** * Extract the account ID from an S3 object key. *
* For example: *
* input: https://s3-us-west-2.amazonaws.com/mybucket/AWSLogs/123456789012/CloudTrail/us-east-1/2014/02/14/123456789012_CloudTrail_us-east-1_20140214T2230Z_K0UsfksWvF8TBJZy.json.gz * output: 1234567890 ** * @param objectKey The object key to query. * @return the account ID used to access the object. */ public static String extractAccountIdFromObjectKey(String objectKey) { if (objectKey == null) { return null; } int start = objectKey.lastIndexOf(FORWARD_SLASH); if (start != -1) { int end = objectKey.indexOf(UNDER_SCORE, start + FORWARD_SLASH.length()); if (end != -1) { return objectKey.substring(start + FORWARD_SLASH.length(), end); } } return null; } /** * Add the account ID attribute to the
sqsMessage
if it does not exist.
* @param sqsMessage The SQS message.
* @param s3ObjectKey The S3 object key.
*/
public static void setMessageAccountId(Message sqsMessage, String s3ObjectKey) {
if (!sqsMessage.getAttributes().containsKey(SourceAttributeKeys.ACCOUNT_ID.getAttributeKey())) {
String accountId = extractAccountIdFromObjectKey(s3ObjectKey);
if (accountId != null) {
sqsMessage.addAttributesEntry(SourceAttributeKeys.ACCOUNT_ID.getAttributeKey(), accountId);
}
}
}
/**
* A wrapper function of handling exceptions that have a known root cause, such as {@link AmazonServiceException}.
* @param exceptionHandler the {@link ExceptionHandler} to handle exceptions.
* @param progressStatus the current progress status {@link ProgressStatus}.
* @param e the exception needs to be handled.
* @param message the exception message.
*/
public static void handleException(ExceptionHandler exceptionHandler, ProgressStatus progressStatus, Exception e, String message) {
ProcessingLibraryException exception = new ProcessingLibraryException(message, e, progressStatus);
exceptionHandler.handleException(exception);
}
/**
* A wrapper function of handling uncaught exceptions.
* @param exceptionHandler the {@link ExceptionHandler} to handle exceptions.
* @param progressStatus the current progress status {@link ProgressStatus}.
* @param message the exception message.
*/
public static void handleException(ExceptionHandler exceptionHandler, ProgressStatus progressStatus, String message) {
ProcessingLibraryException exception = new ProcessingLibraryException(message, progressStatus);
exceptionHandler.handleException(exception);
}
/**
* A wrapper function of reporting the result of the processing.
* @param progressReporter the {@link ProgressReporter} to report the end of process.
* @param processSuccess the result of process.
* @param progressStatus the current progress status {@link ProgressStatus}.
* @param reportObject the object to send, usually the object returned by {@link ProgressReporter#reportStart(ProgressStatus)}.
*/
public static void endToProcess(ProgressReporter progressReporter, boolean processSuccess, ProgressStatus progressStatus, Object reportObject) {
progressStatus.getProgressInfo().setIsSuccess(processSuccess);
progressReporter.reportEnd(progressStatus, reportObject);
}
/**
* SimpleDateFormat is not thread safe. Defining it as a static ThreadLocal to synchronize is less expensive than
* creating a SimpleDateFormat object each time.
*/
private static ThreadLocal