/******************************************************************************* * 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 utcSdf = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat sdf = new SimpleDateFormat(UTC_DATE_FORMAT, Locale.US); // $NON_NLS_L$ sdf.setTimeZone(TimeZone.getTimeZone(UTC_TIME_ZONE)); return sdf; } }; /** * Get a timestamp in * SimpleDateFormat. * * @return the current timestamp. */ public static SimpleDateFormat getUtcSdf() { return utcSdf.get(); } }