From 588b102c326a40c07fe66961d70b2288fe30f79b Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 4 Dec 2023 02:07:04 -0800 Subject: [PATCH 01/19] Adding interfaces for Progress Listener and state capturing Progress Snapshots --- .../ExecutionSuccessObjectRequest.java | 27 ++ .../progress/listener/ProgressListener.java | 395 ++++++++++++++++++ .../listener/snapshot/ListenerProgress.java | 27 ++ .../listener/snapshot/ProgressSnapshot.java | 66 +++ 4 files changed, 515 insertions(+) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java new file mode 100644 index 000000000000..7ae61df403ed --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.listener; + +import software.amazon.awssdk.core.SdkResponse; + +public interface ExecutionSuccessObjectRequest extends ProgressListener.Context.ExecutionSuccess { + /** + * Return the {@link SdkResponse} associated with this request + */ + default SdkResponse response() { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java new file mode 100644 index 000000000000..5384f52d47cd --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java @@ -0,0 +1,395 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.listener; + +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.SdkPreviewApi; +import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.progress.listener.snapshot.ProgressSnapshot; +import software.amazon.awssdk.http.SdkHttpRequest; + +/** + * The {@link ProgressListener} interface may be implemented by your application in order to receive event-driven updates on + * the progress of a service call. When you construct an {@link PutObjectRequest} or {@link + * UploadPartRequest} request submitted to the Sdk, you may provide a variable number of {@link + * ProgressListener}s to be associated with that request. Then, throughout the lifecycle of the request, + * the Sdk + * will invoke the provided {@link ProgressListener}s when important events occur, like additional bytes being transferred, + * allowing you to monitor the ongoing progress of the transaction. + *

+ * Each {@link ProgressListener} callback is invoked with an immutable {@link Context} object. Depending on the current + * lifecycle + * of the request, different {@link Context} objects have different attributes available (indicated by the provided context + * interface). Most notably, every callback is given access to the current {@link ProgressSnapshot}, which contains + * helpful progress-related methods like {@link ProgressSnapshot#transferredBytes()} and {@link + * ProgressSnapshot#ratioTransferred()}. + *

+ * A successful transfer callback lifecycle is sequenced as follows: + *

    + *
  1. {@link #requestPrepared(Context.RequestPrepared)} - A new Request has been initiated. This method is called + * exactly once per transfer.
  2. + * + *
  3. {@link #requestBytesSent(Context.RequestBytesSent)} - Additional bytes have been sent. This + * method may be called many times per request, depending on the request payload size and I/O buffer sizes. + *
  4. {@link #responseBytesReceived(Context.ResponseBytesReceived)} - Additional bytes have been received. This + * method may be called many times per request, depending on the response payload size and I/O buffer sizes. + *
  5. {@link #executionSuccess(Context.ExecutionSuccess)} - The transfer has completed successfully. This method is called + * exactly once for a successful transfer.
  6. + *
+ * For every failed attempt {@link #attemptFailure(Context.AttemptFailure)} will be called exactly once. + * + *

+ * There are a few important rules and best practices that govern the usage of {@link ProgressListener}s: + *

    + *
  1. {@link ProgressListener} implementations should not block, sleep, or otherwise delay the calling thread. If you need + * to perform blocking operations, you should schedule them in a separate thread or executor that you control.
  2. + *
  3. Be mindful that {@link #requestBytesSent(Context.RequestBytesSent)} or + * {@link #responseBytesReceived(Context.ResponseBytesReceived)} + * may be called extremely often for large payloads + * (subject to I/O buffer sizes). Be careful in implementing expensive operations as a side effect. Consider rate-limiting + * your side effect operations, if needed.
  4. + *
  5. {@link ProgressListener}s may be invoked by different threads. If your {@link ProgressListener} is stateful, + * ensure that it is also thread-safe.
  6. + *
  7. {@link ProgressListener}s are not intended to be used for control flow, and therefore your implementation + * should not throw. Any thrown exceptions will be suppressed and logged as an error.
  8. + *
+ *

+ * A classical use case of {@link ProgressListener} is to create a progress bar to monitor an ongoing transfer's progress. + * Refer to the implementation of {@link LoggingProgressListener} for a basic example, or test it in your application by providing + * the listener as part of your {@link SdkHttpRequest}. E.g., + *

{@code
+ * AmazonS3Client s3Client = new AmazonS3Client(awsCredentials);
+ * PutObjectRequest putObjectRequest = PutObjectRequest.builder()
+ *                                        .bucket("bucket")
+ *                                        .key("key")
+ *                                        .overrideConfiguration(o -> o.addProgressListener(LoggingTransferListener.create())
+ *                                        .build();
+ * s3Client.putObject(putObjectRequest);
+ * }
+ * And then a successful transfer may output something similar to: + *
+ * Request initiated...
+ * |                    | 0.0%
+ * |==                  | 12.5%
+ * |=====               | 25.0%
+ * |=======             | 37.5%
+ * |==========          | 50.0%
+ * |============        | 62.5%
+ * |===============     | 75.0%
+ * |=================   | 87.5%
+ * |====================| 100.0%
+ * Request execution successful!
+ * 
+ */ +@SdkPublicApi +public interface ProgressListener { + + /** + * This method is called right after a request object is marshalled and ready to be sent to the service + *

+ * Available context attributes: + *

    + *
  1. {@link Context.RequestPrepared#request()}
  2. + *
  3. {@link Context.RequestPrepared#progressSnapshot()}
  4. + *
+ */ + default void requestPrepared(Context.RequestPrepared context) { + } + + /** + * This method is called after the request transaction is initiated, i.e. request header is sent to the service + *

+ * Available context attributes: + *

    + *
  1. {@link Context.RequestHeaderSent#request()}
  2. + *
  3. {@link Context.RequestHeaderSent#progressSnapshot()}
  4. + *
+ */ + default void requestHeaderSent(Context.RequestHeaderSent context) { + } + + /** + * This method is called with any additional payload bytes sent; it may be called many times per request, depending on the + * payload size. + *

+ * Available context attributes: + *

    + *
  1. {@link Context.RequestBytesSent#request()}
  2. + *
  3. {@link Context.RequestBytesSent#progressSnapshot()}
  4. + *
+ */ + default void requestBytesSent(Context.RequestBytesSent context) { + } + + /** + * The service returns the response headers + *

+ * Available context attributes: + *

    + *
  1. {@link Context.ResponseHeaderReceived#request()}
  2. + *
  3. {@link Context.ResponseHeaderReceived#progressSnapshot()}
  4. + *
+ */ + default void responseHeaderReceived(Context.ResponseHeaderReceived context) { + } + + /** + * Additional bytes received + *

+ * Available context attributes: + *

    + *
  1. {@link Context.ResponseBytesReceived#request()}
  2. + *
  3. {@link Context.ResponseBytesReceived#progressSnapshot()}
  4. + *
+ */ + default void responseBytesReceived(Context.ResponseBytesReceived context) { + } + + /** + * Additional bytes received + *

+ * Available context attributes: + *

    + *
  1. {@link Context.ResponseBytesReceived#request()}
  2. + *
  3. {@link Context.ResponseBytesReceived#progressSnapshot()}
  4. + *
+ */ + default void attemptFailureResponseBytesReceived(Context.AttemptFailureResponseBytesReceived context) { + } + + /** + * Successful request execution + *

+ * Available context attributes: + *

    + *
  1. {@link Context.ExecutionSuccess#request()}
  2. + *
  3. {@link Context.ExecutionSuccess#progressSnapshot()}
  4. + *
  5. {@link Context.ExecutionSuccess#executionSuccess()} ()}
  6. + *
+ */ + default void executionSuccess(Context.ExecutionSuccess context) { + } + + /** + * This method is called for every failure of a request attempt. + * An ideal implementation would invoke executionFailure for a number of attemptFailures greater than a threshold + *

+ * Available context attributes: + *

    + *
  1. {@link Context.AttemptFailure#request()}
  2. + *
  3. {@link Context.AttemptFailure#progressSnapshot()}
  4. + *
+ */ + default void attemptFailure(Context.AttemptFailure context) { + } + + /** + * A wrapper class that groups together the different context interfaces that are exposed to {@link ProgressListener}s. + *

+ * Successful transfer interface hierarchy: + *

    + *
  1. {@link RequestPrepared}
  2. + *
  3. {@link RequestHeaderSent}
  4. + *
  5. {@link RequestBytesSent}
  6. + *
  7. {@link ResponseHeaderReceived}
  8. + *
  9. {@link ResponseBytesReceived}
  10. + *
  11. {@link ExecutionSuccess}
  12. + *
+ * Failed transfer interface hierarchy: + *
    + *
  1. {@link RequestPrepared}
  2. + *
  3. {@link AttemptFailure}
  4. + *
  5. {@link ExecutionFailure}
  6. + *
+ * + * @see ProgressListener + */ + @SdkProtectedApi + final class Context { + private Context() { + } + + /** + * A new transfer has been initiated. + *

+ * Available context attributes: + *

    + *
  1. {@link RequestPrepared#request()}
  2. + *
  3. {@link RequestPrepared#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface RequestPrepared { + /** + * The {@link SdkHttppRequest} that was submitted to SDK, i.e., the {@link PutObjectRequest} or + * {@link GetObjectRequest}. + */ + SdkHttpRequest request(); + + /** + * The immutable {@link ProgressSnapahot} for this specific update. + */ + ProgressSnapshot progressSnapshot(); + } + + /** + * The submitted {@link SdkHttppRequest} request header was successfully sent to the service + *

+ * Available context attributes: + *

    + *
  1. {@link RequestHeaderSent#request()}
  2. + *
  3. {@link RequestHeaderSent#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface RequestHeaderSent extends RequestPrepared { + } + + /** + * Additional bytes sent + *

+ * Available context attributes: + *

    + *
  1. {@link RequestBytesSent#request()}
  2. + *
  3. {@link RequestBytesSent#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface RequestBytesSent extends RequestHeaderSent { + } + + /** + * Service has sent back a response header, denoting the start of response reception + *

+ * Available context attributes: + *

    + *
  1. {@link ResponseHeaderReceived#request()}
  2. + *
  3. {@link ResponseHeaderReceived#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface ResponseHeaderReceived extends RequestBytesSent { + } + + /** + * Additional bytes received + *

+ * Available context attributes: + *

    + *
  1. {@link ResponseBytesReceived#request()}
  2. + *
  3. {@link ResponseBytesReceived#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface ResponseBytesReceived extends ResponseHeaderReceived { + } + + /** + * The request execution is successful. + *

+ * Available context attributes: + *

    + *
  1. {@link ExecutionSuccess#request()}
  2. + *
  3. {@link ExecutionSuccess#progressSnapshot()}
  4. + *
  5. {@link ExecutionSuccess#executionSuccess()} ()}
  6. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface ExecutionSuccess extends ResponseBytesReceived { + /** + * The successful completion of a request submitted to the Sdk + */ + ExecutionSuccessObjectRequest executionSuccess(); + } + + /** + * For Expect: 100-continue embedded requests, the service returning anything other than 100 continue + * indicates a service error. The progress state captured indicates that no bytes are received. + *

+ * Available context attributes: + *

    + *
  1. {@link AttemptFailureResponseBytesReceived#request()}
  2. + *
  3. {@link AttemptFailureResponseBytesReceived#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface AttemptFailureResponseBytesReceived extends ResponseHeaderReceived { + } + + /** + * The request execution attempt failed. + *

+ * Available context attributes: + *

    + *
  1. {@link AttemptFailure#request()}
  2. + *
  3. {@link AttemptFailure#progressSnapshot()}
  4. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface AttemptFailure extends RequestPrepared { + } + + /** + * The request execution failed. + *

+ * Available context attributes: + *

    + *
  1. {@link ExecutionFailure#request()}
  2. + *
  3. {@link ExecutionFailure#progressSnapshot()}
  4. + *
  5. {@link ExecutionFailure#exception()}
  6. + *
+ */ + @Immutable + @ThreadSafe + @SdkPublicApi + @SdkPreviewApi + public interface ExecutionFailure extends RequestPrepared { + /** + * The exception associated with the failed request. + */ + Throwable exception(); + } + } +} \ No newline at end of file diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java new file mode 100644 index 000000000000..62223f2d51e0 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java @@ -0,0 +1,27 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.listener.snapshot; + +public interface ListenerProgress { + + /** + * Takes a snapshot of the request execution progress + * represented by an immutable {@link ProgressSnapshot}. + */ + + ProgressSnapshot progressSnapshot(); + +} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java new file mode 100644 index 000000000000..148df78c84a9 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java @@ -0,0 +1,66 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.listener.snapshot; + +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalLong; +import java.util.concurrent.TimeUnit; + +public interface ProgressSnapshot { + /** + * The total number of bytes that have been sent or received so far. + */ + long transferredBytes(); + + /** + * Time at which transaction started + */ + Instant startTime(); + + /** + * Elapsed time since the start of the transaction + */ + Duration elapsedTime(); + + /** + * If transaction size is known, estimate time remaining for transaction completion + */ + Optional estimatedTimeRemaining(); + + /** + * Rate of transfer + */ + double averageBytesPer(TimeUnit timeUnit); + + /** + * The total size of the transfer, in bytes, or {@link Optional#empty()} if unknown. + */ + OptionalLong totalTransferSize(); + /** + * The ratio of the {@link #totalBytes()} that has been transferred so far, or {@link Optional#empty()} if unknown. + * This method depends on the {@link #totalBytes()} being known in order to return non-empty. + */ + OptionalDouble ratioTransferred(); + + /** + * The total number of bytes that are remaining to be transferred, or {@link Optional#empty()} if unknown. This method depends + * on the {@link #totalBytes()} being known in order to return non-empty. + */ + OptionalLong remainingBytes(); +} From da26d3e9a5633c63469dccd23b4751749efd75b0 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 4 Dec 2023 16:27:42 -0800 Subject: [PATCH 02/19] Modified Indentation --- .../ExecutionSuccessObjectRequest.java | 6 ++++++ .../progress/listener/ProgressListener.java | 18 +++++++++--------- .../listener/snapshot/ListenerProgress.java | 18 ++++++++++++------ .../listener/snapshot/ProgressSnapshot.java | 7 +++++++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java index 7ae61df403ed..05a5be107861 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java @@ -15,9 +15,15 @@ package software.amazon.awssdk.core.progress.listener; +import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.core.SdkResponse; +/** + * A successfully completed single object request. + */ +@SdkPublicApi public interface ExecutionSuccessObjectRequest extends ProgressListener.Context.ExecutionSuccess { + /** * Return the {@link SdkResponse} associated with this request */ diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java index 5384f52d47cd..de3818e4a62d 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java @@ -163,15 +163,15 @@ default void responseHeaderReceived(Context.ResponseHeaderReceived context) { default void responseBytesReceived(Context.ResponseBytesReceived context) { } - /** - * Additional bytes received - *

- * Available context attributes: - *

    - *
  1. {@link Context.ResponseBytesReceived#request()}
  2. - *
  3. {@link Context.ResponseBytesReceived#progressSnapshot()}
  4. - *
- */ + /** + * Additional bytes received + *

+ * Available context attributes: + *

    + *
  1. {@link Context.ResponseBytesReceived#request()}
  2. + *
  3. {@link Context.ResponseBytesReceived#progressSnapshot()}
  4. + *
+ */ default void attemptFailureResponseBytesReceived(Context.AttemptFailureResponseBytesReceived context) { } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java index 62223f2d51e0..ba4433b627a9 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java @@ -15,13 +15,19 @@ package software.amazon.awssdk.core.progress.listener.snapshot; -public interface ListenerProgress { +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; - /** - * Takes a snapshot of the request execution progress - * represented by an immutable {@link ProgressSnapshot}. - */ +@Immutable +@ThreadSafe +@SdkPublicApi +public interface ListenerProgress { - ProgressSnapshot progressSnapshot(); + /** + * Takes a snapshot of the request execution progress + * represented by an immutable {@link ProgressSnapshot}. + */ + ProgressSnapshot progressSnapshot(); } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java index 148df78c84a9..70721f814c38 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java @@ -21,7 +21,13 @@ import java.util.OptionalDouble; import java.util.OptionalLong; import java.util.concurrent.TimeUnit; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +@Immutable +@ThreadSafe +@SdkPublicApi public interface ProgressSnapshot { /** * The total number of bytes that have been sent or received so far. @@ -52,6 +58,7 @@ public interface ProgressSnapshot { * The total size of the transfer, in bytes, or {@link Optional#empty()} if unknown. */ OptionalLong totalTransferSize(); + /** * The ratio of the {@link #totalBytes()} that has been transferred so far, or {@link Optional#empty()} if unknown. * This method depends on the {@link #totalBytes()} being known in order to return non-empty. From 99febb6355d349bc6aa393493cbabd55833f5533 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 4 Dec 2023 16:30:36 -0800 Subject: [PATCH 03/19] Added new change script --- .changes/next-release/feature-AWSSDKforJavav2-b190cbb.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-b190cbb.json diff --git a/.changes/next-release/feature-AWSSDKforJavav2-b190cbb.json b/.changes/next-release/feature-AWSSDKforJavav2-b190cbb.json new file mode 100644 index 000000000000..9580df40a297 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-b190cbb.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "anirudh9391", + "description": "Adding interface methods for Progress Listener and state capturing Progress Snapshots" +} From 7e70224545e0cad83e9f68d5272ce4f3ad7dada1 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 4 Dec 2023 16:32:22 -0800 Subject: [PATCH 04/19] Revert "Added new change script" This reverts commit 99febb6355d349bc6aa393493cbabd55833f5533. --- .changes/next-release/feature-AWSSDKforJavav2-b190cbb.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .changes/next-release/feature-AWSSDKforJavav2-b190cbb.json diff --git a/.changes/next-release/feature-AWSSDKforJavav2-b190cbb.json b/.changes/next-release/feature-AWSSDKforJavav2-b190cbb.json deleted file mode 100644 index 9580df40a297..000000000000 --- a/.changes/next-release/feature-AWSSDKforJavav2-b190cbb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "type": "feature", - "category": "AWS SDK for Java v2", - "contributor": "anirudh9391", - "description": "Adding interface methods for Progress Listener and state capturing Progress Snapshots" -} From 5e9057e1d6bac8dc563b478ea2db38add1a05107 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 4 Dec 2023 16:32:31 -0800 Subject: [PATCH 05/19] Added new change script --- .changes/next-release/feature-AWSSDKforJavav2-83dc9e4.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-83dc9e4.json diff --git a/.changes/next-release/feature-AWSSDKforJavav2-83dc9e4.json b/.changes/next-release/feature-AWSSDKforJavav2-83dc9e4.json new file mode 100644 index 000000000000..5e66a5c18528 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-83dc9e4.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "anirudh9391", + "description": "General Progress Listeners" +} From e607d6ca39b7640681b88bc49b8df5b8e1f6ed13 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 4 Dec 2023 17:11:43 -0800 Subject: [PATCH 06/19] Moved ProgressSnapshot to its own folder --- .../amazon/awssdk/core/progress/listener/ProgressListener.java | 2 +- .../core/progress/{listener => }/snapshot/ListenerProgress.java | 2 +- .../core/progress/{listener => }/snapshot/ProgressSnapshot.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/{listener => }/snapshot/ListenerProgress.java (93%) rename core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/{listener => }/snapshot/ProgressSnapshot.java (97%) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java index de3818e4a62d..40f7c036f15f 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java @@ -20,7 +20,7 @@ import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.progress.listener.snapshot.ProgressSnapshot; +import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; import software.amazon.awssdk.http.SdkHttpRequest; /** diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java similarity index 93% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java index ba4433b627a9..e1692fa27752 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ListenerProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.listener.snapshot; +package software.amazon.awssdk.core.progress.snapshot; import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkPublicApi; diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java similarity index 97% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index 70721f814c38..89f5fb138819 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.listener.snapshot; +package software.amazon.awssdk.core.progress.snapshot; import java.time.Duration; import java.time.Instant; From 3d26526821ff824d5874837c970d9d6ab75553a9 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Tue, 5 Dec 2023 15:35:34 -0800 Subject: [PATCH 07/19] Added sdkResponse to Progress Snapshot --- .../core/progress/snapshot/DefaultProgressSnapshot.java | 2 ++ .../awssdk/core/progress/snapshot/ProgressSnapshot.java | 6 ++++++ 2 files changed, 8 insertions(+) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java new file mode 100644 index 000000000000..074fbea14010 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java @@ -0,0 +1,2 @@ +package software.amazon.awssdk.core.progress.snapshot;public class DefaultProgressSnapshot { +} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index 89f5fb138819..e17c3bf48ddf 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -24,6 +24,7 @@ import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.SdkResponse; @Immutable @ThreadSafe @@ -49,6 +50,11 @@ public interface ProgressSnapshot { */ Optional estimatedTimeRemaining(); + /** + * The SDK response, or {@link Optional#empty()} if unknown. + */ + Optional sdkResponse(); + /** * Rate of transfer */ From 48a66175bf7f5b0d717a4ec80475a2fab150f31d Mon Sep 17 00:00:00 2001 From: Krishnan Date: Tue, 5 Dec 2023 15:53:40 -0800 Subject: [PATCH 08/19] Change totalTransferSize to totalBytes --- .../amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index e17c3bf48ddf..038ea2777956 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -63,7 +63,7 @@ public interface ProgressSnapshot { /** * The total size of the transfer, in bytes, or {@link Optional#empty()} if unknown. */ - OptionalLong totalTransferSize(); + OptionalLong totalBytes(); /** * The ratio of the {@link #totalBytes()} that has been transferred so far, or {@link Optional#empty()} if unknown. From b0967658dac900533a959f551bf377a07fda389e Mon Sep 17 00:00:00 2001 From: Krishnan Date: Tue, 5 Dec 2023 16:28:35 -0800 Subject: [PATCH 09/19] Modify return types of start and elapsedTime to Optional --- .../awssdk/core/progress/snapshot/ProgressSnapshot.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index 038ea2777956..2a037519a8be 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -38,12 +38,12 @@ public interface ProgressSnapshot { /** * Time at which transaction started */ - Instant startTime(); + Optional startTime(); /** * Elapsed time since the start of the transaction */ - Duration elapsedTime(); + Optional elapsedTime(); /** * If transaction size is known, estimate time remaining for transaction completion From b558e6bc12d54179ebc7f84e0e7e9d019e07c9dc Mon Sep 17 00:00:00 2001 From: Krishnan Date: Thu, 7 Dec 2023 20:15:39 -0800 Subject: [PATCH 10/19] Progress Listener Interface definition --- .../ExecutionSuccessObjectRequest.java | 33 ---- .../progress/listener/ProgressListener.java | 175 ++++++++++++++---- .../snapshot/DefaultProgressSnapshot.java | 2 - .../progress/snapshot/ListenerProgress.java | 1 - .../progress/snapshot/ProgressSnapshot.java | 19 +- 5 files changed, 144 insertions(+), 86 deletions(-) delete mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java delete mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java deleted file mode 100644 index 05a5be107861..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ExecutionSuccessObjectRequest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 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 software.amazon.awssdk.core.progress.listener; - -import software.amazon.awssdk.annotations.SdkPublicApi; -import software.amazon.awssdk.core.SdkResponse; - -/** - * A successfully completed single object request. - */ -@SdkPublicApi -public interface ExecutionSuccessObjectRequest extends ProgressListener.Context.ExecutionSuccess { - - /** - * Return the {@link SdkResponse} associated with this request - */ - default SdkResponse response() { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java index 40f7c036f15f..a78fd8f01672 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java @@ -20,29 +20,34 @@ import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.SdkResponse; import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.SdkHttpResponse; /** * The {@link ProgressListener} interface may be implemented by your application in order to receive event-driven updates on - * the progress of a service call. When you construct an {@link PutObjectRequest} or {@link - * UploadPartRequest} request submitted to the Sdk, you may provide a variable number of {@link - * ProgressListener}s to be associated with that request. Then, throughout the lifecycle of the request, - * the Sdk - * will invoke the provided {@link ProgressListener}s when important events occur, like additional bytes being transferred, - * allowing you to monitor the ongoing progress of the transaction. + * the progress of a service call. When an {@link SdkRequest} like {@link PutObjectRequest} or {@link + * UploadPartRequest} is submitted to the Sdk, you may provide a variable number of {@link + * ProgressListener}s to be associated with that request. + *

+ * While ExecutionInterceptors are focused on the lifecycle of the + * request within the SDK, ProgressListeners are focused on the lifecycle of the request within the HTTP client. Throughout + * the lifecycle of the client request to which it is attached, the Sdk will invoke the provided {@link ProgressListener}s when + * important events occur, like additional bytes being transferred, allowing you to monitor the ongoing progress of the + * transaction. *

* Each {@link ProgressListener} callback is invoked with an immutable {@link Context} object. Depending on the current - * lifecycle - * of the request, different {@link Context} objects have different attributes available (indicated by the provided context - * interface). Most notably, every callback is given access to the current {@link ProgressSnapshot}, which contains + * lifecycle of the request, different {@link Context} objects have different attributes available (indicated by the provided + * context interface). Most notably, every callback is given access to the current {@link ProgressSnapshot}, which contains * helpful progress-related methods like {@link ProgressSnapshot#transferredBytes()} and {@link * ProgressSnapshot#ratioTransferred()}. *

* A successful transfer callback lifecycle is sequenced as follows: *

    - *
  1. {@link #requestPrepared(Context.RequestPrepared)} - A new Request has been initiated. This method is called - * exactly once per transfer.
  2. + *
  3. {@link #requestPrepared(Context.RequestPrepared)} - This method is called for every newly initiated SdkRequest, + * after it is marshalled and signed, before it is sent to the service
  4. *
      Available context attributes: *
    • {@link Context.RequestPrepared#request()}
    • *
    • {@link Context.RequestPrepared#progressSnapshot()}
    • @@ -52,9 +57,9 @@ *
    • {@link #responseBytesReceived(Context.ResponseBytesReceived)} - Additional bytes have been received. This * method may be called many times per request, depending on the response payload size and I/O buffer sizes. *
    • {@link #executionSuccess(Context.ExecutionSuccess)} - The transfer has completed successfully. This method is called - * exactly once for a successful transfer.
    • + * for every successful transfer. *
- * For every failed attempt {@link #attemptFailure(Context.AttemptFailure)} will be called exactly once. + * For every failed attempt {@link #attemptFailure(Context.AttemptFailure)}. * *

* There are a few important rules and best practices that govern the usage of {@link ProgressListener}s: @@ -103,11 +108,14 @@ public interface ProgressListener { /** - * This method is called right after a request object is marshalled and ready to be sent to the service + * This method is called right after a {@link SdkRequest} is marshalled, signed, transformed into an {@link SdkHttpRequest} and ready to + * be sent to the service + * After this method has returned, either requestHeaderSent or executionFailure will always be invoked *

* Available context attributes: *

    *
  1. {@link Context.RequestPrepared#request()}
  2. + *
  3. {@link Context.RequestPrepared#httpRequest()}
  4. *
  5. {@link Context.RequestPrepared#progressSnapshot()}
  6. *
*/ @@ -116,10 +124,13 @@ default void requestPrepared(Context.RequestPrepared context) { /** * This method is called after the request transaction is initiated, i.e. request header is sent to the service + * After this method, one among requestBytesSent, responseHeaderReceived, and attemptFailure will be always be + * invoked *

* Available context attributes: *

    *
  1. {@link Context.RequestHeaderSent#request()}
  2. + *
  3. {@link Context.RequestHeaderSent#httpRequest()}
  4. *
  5. {@link Context.RequestHeaderSent#progressSnapshot()}
  6. *
*/ @@ -129,10 +140,12 @@ default void requestHeaderSent(Context.RequestHeaderSent context) { /** * This method is called with any additional payload bytes sent; it may be called many times per request, depending on the * payload size. + * After this method, either responseHeaderReceived or attemptFailure will always be invoked *

* Available context attributes: *

    *
  1. {@link Context.RequestBytesSent#request()}
  2. + *
  3. {@link Context.RequestBytesSent#httpRequest()}
  4. *
  5. {@link Context.RequestBytesSent#progressSnapshot()}
  6. *
*/ @@ -141,11 +154,14 @@ default void requestBytesSent(Context.RequestBytesSent context) { /** * The service returns the response headers + * After this, one among responseBytesReceived, attemptFailureResponseBytesReceived and attemptFailure will always be invoked *

* Available context attributes: *

    *
  1. {@link Context.ResponseHeaderReceived#request()}
  2. + *
  3. {@link Context.ResponseHeaderReceived#httpRequest()}
  4. *
  5. {@link Context.ResponseHeaderReceived#progressSnapshot()}
  6. + *
  7. {@link Context.ResponseHeaderReceived#httpResponse()} ()}
  8. *
*/ default void responseHeaderReceived(Context.ResponseHeaderReceived context) { @@ -153,23 +169,31 @@ default void responseHeaderReceived(Context.ResponseHeaderReceived context) { /** * Additional bytes received + * After this, either executionSuccess or attemptFailure will always be invoked *

* Available context attributes: *

    *
  1. {@link Context.ResponseBytesReceived#request()}
  2. + *
  3. {@link Context.ResponseBytesReceived#httpRequest()}
  4. *
  5. {@link Context.ResponseBytesReceived#progressSnapshot()}
  6. + *
  7. {@link Context.ResponseBytesReceived#httpResponse()}
  8. *
*/ default void responseBytesReceived(Context.ResponseBytesReceived context) { } /** - * Additional bytes received + * For Expect: 100-continue embedded requests, the service returning anything other than 100 continue + * indicates a request failure. This method captures the error in the payload + * After this, either executionFailure or requestHeaderSent will always be invoked depending on + * whether the error type is retryable or not *

* Available context attributes: *

    - *
  1. {@link Context.ResponseBytesReceived#request()}
  2. - *
  3. {@link Context.ResponseBytesReceived#progressSnapshot()}
  4. + *
  5. {@link Context.AttemptFailureResponseBytesReceived#request()}
  6. + *
  7. {@link Context.AttemptFailureResponseBytesReceived#httpRequest()}
  8. + *
  9. {@link Context.AttemptFailureResponseBytesReceived#progressSnapshot()}
  10. + *
  11. {@link Context.AttemptFailureResponseBytesReceived#httpResponse()} ()}
  12. *
*/ default void attemptFailureResponseBytesReceived(Context.AttemptFailureResponseBytesReceived context) { @@ -177,30 +201,51 @@ default void attemptFailureResponseBytesReceived(Context.AttemptFailureResponseB /** * Successful request execution + * This marks the end of the request path. *

* Available context attributes: *

    *
  1. {@link Context.ExecutionSuccess#request()}
  2. + *
  3. {@link Context.ExecutionSuccess#httpRequest()}
  4. *
  5. {@link Context.ExecutionSuccess#progressSnapshot()}
  6. - *
  7. {@link Context.ExecutionSuccess#executionSuccess()} ()}
  8. + *
  9. {@link Context.ExecutionSuccess#httpResponse()}
  10. + *
  11. {@link Context.ExecutionSuccess#response()}
  12. *
*/ default void executionSuccess(Context.ExecutionSuccess context) { } /** - * This method is called for every failure of a request attempt. - * An ideal implementation would invoke executionFailure for a number of attemptFailures greater than a threshold + * This method is called for every failure of a request attempt + * This method is followed by either a retry attempt which would be requestHeaderSent, + * or an executionFailure if it has exceeded the maximum number of retries configured *

* Available context attributes: *

    *
  1. {@link Context.AttemptFailure#request()}
  2. + *
  3. {@link Context.AttemptFailure#httpRequest()}
  4. *
  5. {@link Context.AttemptFailure#progressSnapshot()}
  6. + *
  7. {@link Context.AttemptFailure#exception()} ()}
  8. *
*/ default void attemptFailure(Context.AttemptFailure context) { } + /** + * This method is called for every failed request execution + * This marks end of the request path with an exception being throw with the appropriate message + *

+ * Available context attributes: + *

    + *
  1. {@link Context.ExecutionFailure#request()}
  2. + *
  3. {@link Context.ExecutionFailure#httpRequest()}
  4. + *
  5. {@link Context.ExecutionFailure#progressSnapshot()}
  6. + *
  7. {@link Context.ExecutionFailure#exception()} ()}
  8. + *
+ */ + default void executionFailure(Context.ExecutionFailure context) { + } + /** * A wrapper class that groups together the different context interfaces that are exposed to {@link ProgressListener}s. *

@@ -213,12 +258,22 @@ default void attemptFailure(Context.AttemptFailure context) { *

  • {@link ResponseBytesReceived}
  • *
  • {@link ExecutionSuccess}
  • * - * Failed transfer interface hierarchy: + * Failed transfer method hierarchy: *
      *
    1. {@link RequestPrepared}
    2. *
    3. {@link AttemptFailure}
    4. *
    5. {@link ExecutionFailure}
    6. *
    + * If the request header includes an Expect: 100-Continue and the service returns a different value, the method invokation + * hierarchy is as follows : + *
      + *
    1. {@link RequestPrepared}
    2. + *
    3. {@link RequestHeaderSent}
    4. + *
    5. {@link RequestBytesSent}
    6. + *
    7. {@link ResponseHeaderReceived}
    8. + *
    9. {@link AttemptFailureResponseBytesReceived}
    10. + *
    11. {@link ExecutionFailure}
    12. + *
    * * @see ProgressListener */ @@ -242,24 +297,32 @@ private Context() { @SdkPreviewApi public interface RequestPrepared { /** - * The {@link SdkHttppRequest} that was submitted to SDK, i.e., the {@link PutObjectRequest} or - * {@link GetObjectRequest}. + * The {@link SdkRequest} that was submitted to SDK, i.e., the {@link PutObjectRequest} or + * {@link GetObjectRequest} */ - SdkHttpRequest request(); + SdkRequest request(); /** - * The immutable {@link ProgressSnapahot} for this specific update. + * The {@link SdkRequest} that was submitted to SDK, i.e., the {@link PutObjectRequest} or + * {@link GetObjectRequest} is marshalled, signed and transformed into an {@link SdkHttpRequest} + * */ - ProgressSnapshot progressSnapshot(); + SdkHttpRequest httpRequest(); + + /** + * The immutable {@link ProgressSnapshot} to track upload progress state + */ + ProgressSnapshot uploadProgressSnapshot(); } /** - * The submitted {@link SdkHttppRequest} request header was successfully sent to the service + * The submitted {@link SdkHttpRequest} request header was successfully sent to the service *

    * Available context attributes: *

      *
    1. {@link RequestHeaderSent#request()}
    2. - *
    3. {@link RequestHeaderSent#progressSnapshot()}
    4. + *
    5. {@link RequestHeaderSent#httpRequest()}
    6. + *
    7. {@link RequestHeaderSent#uploadProgressSnapshot()}
    8. *
    */ @Immutable @@ -275,7 +338,8 @@ public interface RequestHeaderSent extends RequestPrepared { * Available context attributes: *
      *
    1. {@link RequestBytesSent#request()}
    2. - *
    3. {@link RequestBytesSent#progressSnapshot()}
    4. + *
    5. {@link RequestBytesSent#httpRequest()}
    6. + *
    7. {@link RequestBytesSent#uploadProgressSnapshot()}
    8. *
    */ @Immutable @@ -291,7 +355,9 @@ public interface RequestBytesSent extends RequestHeaderSent { * Available context attributes: *
      *
    1. {@link ResponseHeaderReceived#request()}
    2. - *
    3. {@link ResponseHeaderReceived#progressSnapshot()}
    4. + *
    5. {@link ResponseHeaderReceived#httpRequest()}
    6. + *
    7. {@link ResponseHeaderReceived#uploadProgressSnapshot()}
    8. + *
    9. {@link ResponseHeaderReceived#httpResponse()}
    10. *
    */ @Immutable @@ -299,6 +365,12 @@ public interface RequestBytesSent extends RequestHeaderSent { @SdkPublicApi @SdkPreviewApi public interface ResponseHeaderReceived extends RequestBytesSent { + SdkHttpResponse httpResponse(); + + /** + * The immutable {@link ProgressSnapshot} to track download progress state + */ + ProgressSnapshot downloadProgressSnapshot(); } /** @@ -307,7 +379,10 @@ public interface ResponseHeaderReceived extends RequestBytesSent { * Available context attributes: *
      *
    1. {@link ResponseBytesReceived#request()}
    2. - *
    3. {@link ResponseBytesReceived#progressSnapshot()}
    4. + *
    5. {@link ResponseBytesReceived#httpRequest()} ()}
    6. + *
    7. {@link ResponseBytesReceived#uploadProgressSnapshot()}
    8. + *
    9. {@link ResponseBytesReceived#httpResponse()}
    10. + *
    11. {@link ResponseBytesReceived#downloadProgressSnapshot()}
    12. *
    */ @Immutable @@ -323,8 +398,11 @@ public interface ResponseBytesReceived extends ResponseHeaderReceived { * Available context attributes: *
      *
    1. {@link ExecutionSuccess#request()}
    2. - *
    3. {@link ExecutionSuccess#progressSnapshot()}
    4. - *
    5. {@link ExecutionSuccess#executionSuccess()} ()}
    6. + *
    7. {@link ExecutionSuccess#httpRequest()}
    8. + *
    9. {@link ExecutionSuccess#uploadProgressSnapshot()}
    10. + *
    11. {@link ExecutionSuccess#httpResponse()}
    12. + *
    13. {@link ExecutionSuccess#downloadProgressSnapshot()}
    14. + *
    15. {@link ExecutionSuccess#response()}
    16. *
    */ @Immutable @@ -335,17 +413,20 @@ public interface ExecutionSuccess extends ResponseBytesReceived { /** * The successful completion of a request submitted to the Sdk */ - ExecutionSuccessObjectRequest executionSuccess(); + SdkResponse response(); } /** - * For Expect: 100-continue embedded requests, the service returning anything other than 100 continue - * indicates a service error. The progress state captured indicates that no bytes are received. + * This facilitates capturing and handling an error response returned by service *

    * Available context attributes: *

      *
    1. {@link AttemptFailureResponseBytesReceived#request()}
    2. - *
    3. {@link AttemptFailureResponseBytesReceived#progressSnapshot()}
    4. + *
    5. {@link AttemptFailureResponseBytesReceived#httpRequest()}
    6. + *
    7. {@link AttemptFailureResponseBytesReceived#uploadProgressSnapshot()}
    8. + *
    9. {@link AttemptFailureResponseBytesReceived#httpResponse()} ()}
    10. + *
    11. {@link AttemptFailureResponseBytesReceived#downloadProgressSnapshot()}
    12. + *
    13. {@link AttemptFailureResponseBytesReceived#exception()}
    14. *
    */ @Immutable @@ -353,6 +434,7 @@ public interface ExecutionSuccess extends ResponseBytesReceived { @SdkPublicApi @SdkPreviewApi public interface AttemptFailureResponseBytesReceived extends ResponseHeaderReceived { + Throwable exception(); } /** @@ -361,14 +443,22 @@ public interface AttemptFailureResponseBytesReceived extends ResponseHeaderRecei * Available context attributes: *
      *
    1. {@link AttemptFailure#request()}
    2. - *
    3. {@link AttemptFailure#progressSnapshot()}
    4. + *
    5. {@link AttemptFailure#httpRequest()}
    6. + *
    7. {@link AttemptFailure#uploadProgressSnapshot()}
    8. + *
    9. {@link AttemptFailure#httpResponse()}
    10. + *
    11. {@link AttemptFailure#downloadProgressSnapshot()}
    12. + *
    13. {@link AttemptFailure#exception()}
    14. *
    */ @Immutable @ThreadSafe @SdkPublicApi @SdkPreviewApi - public interface AttemptFailure extends RequestPrepared { + public interface AttemptFailure extends ResponseBytesReceived { + /** + * The exception associated with the failed request. + */ + Throwable exception(); } /** @@ -377,7 +467,10 @@ public interface AttemptFailure extends RequestPrepared { * Available context attributes: *
      *
    1. {@link ExecutionFailure#request()}
    2. - *
    3. {@link ExecutionFailure#progressSnapshot()}
    4. + *
    5. {@link ExecutionFailure#httpRequest()}
    6. + *
    7. {@link ExecutionFailure#uploadProgressSnapshot()}
    8. + *
    9. {@link AttemptFailure#httpResponse()}
    10. + *
    11. {@link ExecutionFailure#downloadProgressSnapshot()}
    12. *
    13. {@link ExecutionFailure#exception()}
    14. *
    */ @@ -385,7 +478,7 @@ public interface AttemptFailure extends RequestPrepared { @ThreadSafe @SdkPublicApi @SdkPreviewApi - public interface ExecutionFailure extends RequestPrepared { + public interface ExecutionFailure extends ResponseBytesReceived { /** * The exception associated with the failed request. */ diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java deleted file mode 100644 index 074fbea14010..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java +++ /dev/null @@ -1,2 +0,0 @@ -package software.amazon.awssdk.core.progress.snapshot;public class DefaultProgressSnapshot { -} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java index e1692fa27752..71439266246f 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java @@ -28,6 +28,5 @@ public interface ListenerProgress { * Takes a snapshot of the request execution progress * represented by an immutable {@link ProgressSnapshot}. */ - ProgressSnapshot progressSnapshot(); } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index 2a037519a8be..af314248b8b0 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -36,32 +36,33 @@ public interface ProgressSnapshot { long transferredBytes(); /** - * Time at which transaction started + * Time at which the HTTP Request header is sent */ Optional startTime(); /** - * Elapsed time since the start of the transaction + * Elapsed time since the HTTP request header was sent to the service */ Optional elapsedTime(); /** * If transaction size is known, estimate time remaining for transaction completion + * This is a predictive calculation based on the rate of transfer + *

    + * Double rateOfTimeUnitsPerByte = elapsedTime() / transferredBytes(); + * Double estimatedTimeRemaining = rateOfTimeUnitsPerByte * (totalBytes() - transferredBytes()); + *

    */ Optional estimatedTimeRemaining(); - /** - * The SDK response, or {@link Optional#empty()} if unknown. - */ - Optional sdkResponse(); - /** * Rate of transfer */ - double averageBytesPer(TimeUnit timeUnit); + OptionalDouble averageBytesPer(TimeUnit timeUnit); /** - * The total size of the transfer, in bytes, or {@link Optional#empty()} if unknown. + * The total size of the transfer, in bytes, or {@link Optional#empty()} if total payload being transacted is unknown + * and not set. This could happen for streaming operations. */ OptionalLong totalBytes(); From 07f2bfb07b089bc48349070a9422dffdee503f00 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Thu, 7 Dec 2023 20:38:38 -0800 Subject: [PATCH 11/19] Address PR comments on Progress Listener --- .../progress/listener/ProgressListener.java | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java index a78fd8f01672..98f6159b6901 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java @@ -50,7 +50,7 @@ * after it is marshalled and signed, before it is sent to the service *
      Available context attributes: *
    • {@link Context.RequestPrepared#request()}
    • - *
    • {@link Context.RequestPrepared#progressSnapshot()}
    • + *
    • {@link Context.RequestPrepared#uploadProgressSnapshot()}
    • *
    *
  • {@link #requestBytesSent(Context.RequestBytesSent)} - Additional bytes have been sent. This * method may be called many times per request, depending on the request payload size and I/O buffer sizes. @@ -116,7 +116,7 @@ public interface ProgressListener { *
      *
    1. {@link Context.RequestPrepared#request()}
    2. *
    3. {@link Context.RequestPrepared#httpRequest()}
    4. - *
    5. {@link Context.RequestPrepared#progressSnapshot()}
    6. + *
    7. {@link Context.RequestPrepared#uploadProgressSnapshot()}
    8. *
    */ default void requestPrepared(Context.RequestPrepared context) { @@ -131,7 +131,7 @@ default void requestPrepared(Context.RequestPrepared context) { *
      *
    1. {@link Context.RequestHeaderSent#request()}
    2. *
    3. {@link Context.RequestHeaderSent#httpRequest()}
    4. - *
    5. {@link Context.RequestHeaderSent#progressSnapshot()}
    6. + *
    7. {@link Context.RequestHeaderSent#uploadProgressSnapshot()}
    8. *
    */ default void requestHeaderSent(Context.RequestHeaderSent context) { @@ -146,7 +146,7 @@ default void requestHeaderSent(Context.RequestHeaderSent context) { *
      *
    1. {@link Context.RequestBytesSent#request()}
    2. *
    3. {@link Context.RequestBytesSent#httpRequest()}
    4. - *
    5. {@link Context.RequestBytesSent#progressSnapshot()}
    6. + *
    7. {@link Context.RequestBytesSent#uploadProgressSnapshot()}
    8. *
    */ default void requestBytesSent(Context.RequestBytesSent context) { @@ -160,8 +160,9 @@ default void requestBytesSent(Context.RequestBytesSent context) { *
      *
    1. {@link Context.ResponseHeaderReceived#request()}
    2. *
    3. {@link Context.ResponseHeaderReceived#httpRequest()}
    4. - *
    5. {@link Context.ResponseHeaderReceived#progressSnapshot()}
    6. + *
    7. {@link Context.ResponseHeaderReceived#uploadProgressSnapshot()}
    8. *
    9. {@link Context.ResponseHeaderReceived#httpResponse()} ()}
    10. + *
    11. {@link Context.ResponseHeaderReceived#downloadProgressSnapshot()}
    12. *
    */ default void responseHeaderReceived(Context.ResponseHeaderReceived context) { @@ -175,8 +176,9 @@ default void responseHeaderReceived(Context.ResponseHeaderReceived context) { *
      *
    1. {@link Context.ResponseBytesReceived#request()}
    2. *
    3. {@link Context.ResponseBytesReceived#httpRequest()}
    4. - *
    5. {@link Context.ResponseBytesReceived#progressSnapshot()}
    6. + *
    7. {@link Context.ResponseBytesReceived#uploadProgressSnapshot()}
    8. *
    9. {@link Context.ResponseBytesReceived#httpResponse()}
    10. + *
    11. {@link Context.ResponseBytesReceived#downloadProgressSnapshot()}
    12. *
    */ default void responseBytesReceived(Context.ResponseBytesReceived context) { @@ -192,8 +194,10 @@ default void responseBytesReceived(Context.ResponseBytesReceived context) { *
      *
    1. {@link Context.AttemptFailureResponseBytesReceived#request()}
    2. *
    3. {@link Context.AttemptFailureResponseBytesReceived#httpRequest()}
    4. - *
    5. {@link Context.AttemptFailureResponseBytesReceived#progressSnapshot()}
    6. + *
    7. {@link Context.AttemptFailureResponseBytesReceived#uploadProgressSnapshot()}
    8. *
    9. {@link Context.AttemptFailureResponseBytesReceived#httpResponse()} ()}
    10. + *
    11. {@link Context.AttemptFailureResponseBytesReceived#downloadProgressSnapshot()}
    12. + *
    13. {@link Context.AttemptFailureResponseBytesReceived#exception()}
    14. *
    */ default void attemptFailureResponseBytesReceived(Context.AttemptFailureResponseBytesReceived context) { @@ -207,8 +211,9 @@ default void attemptFailureResponseBytesReceived(Context.AttemptFailureResponseB *
      *
    1. {@link Context.ExecutionSuccess#request()}
    2. *
    3. {@link Context.ExecutionSuccess#httpRequest()}
    4. - *
    5. {@link Context.ExecutionSuccess#progressSnapshot()}
    6. + *
    7. {@link Context.ExecutionSuccess#uploadProgressSnapshot()}
    8. *
    9. {@link Context.ExecutionSuccess#httpResponse()}
    10. + *
    11. {@link Context.ExecutionSuccess#downloadProgressSnapshot()}
    12. *
    13. {@link Context.ExecutionSuccess#response()}
    14. *
    */ @@ -224,7 +229,7 @@ default void executionSuccess(Context.ExecutionSuccess context) { *
      *
    1. {@link Context.AttemptFailure#request()}
    2. *
    3. {@link Context.AttemptFailure#httpRequest()}
    4. - *
    5. {@link Context.AttemptFailure#progressSnapshot()}
    6. + *
    7. {@link Context.AttemptFailure#uploadProgressSnapshot()}
    8. *
    9. {@link Context.AttemptFailure#exception()} ()}
    10. *
    */ @@ -239,7 +244,7 @@ default void attemptFailure(Context.AttemptFailure context) { *
      *
    1. {@link Context.ExecutionFailure#request()}
    2. *
    3. {@link Context.ExecutionFailure#httpRequest()}
    4. - *
    5. {@link Context.ExecutionFailure#progressSnapshot()}
    6. + *
    7. {@link Context.ExecutionFailure#uploadProgressSnapshot()}
    8. *
    9. {@link Context.ExecutionFailure#exception()} ()}
    10. *
    */ @@ -445,8 +450,6 @@ public interface AttemptFailureResponseBytesReceived extends ResponseHeaderRecei *
  • {@link AttemptFailure#request()}
  • *
  • {@link AttemptFailure#httpRequest()}
  • *
  • {@link AttemptFailure#uploadProgressSnapshot()}
  • - *
  • {@link AttemptFailure#httpResponse()}
  • - *
  • {@link AttemptFailure#downloadProgressSnapshot()}
  • *
  • {@link AttemptFailure#exception()}
  • * */ @@ -454,7 +457,7 @@ public interface AttemptFailureResponseBytesReceived extends ResponseHeaderRecei @ThreadSafe @SdkPublicApi @SdkPreviewApi - public interface AttemptFailure extends ResponseBytesReceived { + public interface AttemptFailure extends RequestPrepared { /** * The exception associated with the failed request. */ @@ -469,8 +472,6 @@ public interface AttemptFailure extends ResponseBytesReceived { *
  • {@link ExecutionFailure#request()}
  • *
  • {@link ExecutionFailure#httpRequest()}
  • *
  • {@link ExecutionFailure#uploadProgressSnapshot()}
  • - *
  • {@link AttemptFailure#httpResponse()}
  • - *
  • {@link ExecutionFailure#downloadProgressSnapshot()}
  • *
  • {@link ExecutionFailure#exception()}
  • * */ @@ -478,7 +479,7 @@ public interface AttemptFailure extends ResponseBytesReceived { @ThreadSafe @SdkPublicApi @SdkPreviewApi - public interface ExecutionFailure extends ResponseBytesReceived { + public interface ExecutionFailure extends RequestPrepared { /** * The exception associated with the failed request. */ From d2a325322a4e8685aee1a2610871354c3caf38d4 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 11 Dec 2023 10:08:39 -0800 Subject: [PATCH 12/19] Fix checkstyle issues --- .../amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index af314248b8b0..829ce35b0dc0 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -24,7 +24,6 @@ import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.SdkResponse; @Immutable @ThreadSafe From eaf94708e401de421c0915f92aaa70cae1e13407 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Mon, 11 Dec 2023 10:47:40 -0800 Subject: [PATCH 13/19] Fix checkstyle issues --- .../awssdk/core/progress/listener/ProgressListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java index 98f6159b6901..ee0cff4d249e 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/ProgressListener.java @@ -108,8 +108,8 @@ public interface ProgressListener { /** - * This method is called right after a {@link SdkRequest} is marshalled, signed, transformed into an {@link SdkHttpRequest} and ready to - * be sent to the service + * This method is called right after a {@link SdkRequest} is marshalled, signed, transformed into an + * {@link SdkHttpRequest} and ready to be sent to the service * After this method has returned, either requestHeaderSent or executionFailure will always be invoked *

    * Available context attributes: From 276fb70834a56be09e5d0805b55545c85843f369 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Thu, 14 Dec 2023 02:41:39 -0800 Subject: [PATCH 14/19] Implement Default Progress Snapshot --- core/sdk-core/pom.xml | 8 + .../listener/DefaultSdkRequestProgress.java | 56 +++++ .../SdkRequestProgress.java} | 5 +- .../snapshot/DefaultProgressSnapshot.java | 174 +++++++++++++++ .../progress/DefaultProgressSnapshotTest.java | 202 ++++++++++++++++++ 5 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java rename core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/{snapshot/ListenerProgress.java => listener/SdkRequestProgress.java} (85%) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java create mode 100644 core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java diff --git a/core/sdk-core/pom.xml b/core/sdk-core/pom.xml index f19120fde62d..d15be86a3608 100644 --- a/core/sdk-core/pom.xml +++ b/core/sdk-core/pom.xml @@ -284,6 +284,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java new file mode 100644 index 000000000000..6c8a602afcc6 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java @@ -0,0 +1,56 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.listener; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Mutable; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; +import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; + +/** + * An SDK-internal implementation of {@link SdkRequestProgress}. This implementation acts as a thin wrapper around {@link + * AtomicReference}, where calls to get the latest {@link #progressSnapshot()} simply return the latest reference, while {@link + * SdkRequestProgressUpdater} is responsible for continuously updating the latest reference. + * + * @see SdkRequestProgress + */ +@Mutable +@ThreadSafe +@SdkInternalApi +public class DefaultSdkRequestProgress implements SdkRequestProgress { + + private final AtomicReference snapshot; + + public DefaultSdkRequestProgress(ProgressSnapshot snapshot) { + this.snapshot = new AtomicReference<>(snapshot); + } + + /** + * Atomically convert the current snapshot reference to its {@link Builder}, perform updates using the provided {@link + * Consumer}, and save the result as the latest snapshot. + */ + public ProgressSnapshot updateAndGet(Consumer updater) { + return this.snapshot.updateAndGet(s -> ((DefaultProgressSnapshot) s).copy(updater)); + } + + @Override + public ProgressSnapshot progressSnapshot() { + return this.snapshot.get(); + } +} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java similarity index 85% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java index 71439266246f..837b30535868 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java @@ -13,16 +13,17 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.snapshot; +package software.amazon.awssdk.core.progress.listener; import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; @Immutable @ThreadSafe @SdkPublicApi -public interface ListenerProgress { +public interface SdkRequestProgress { /** * Takes a snapshot of the request execution progress diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java new file mode 100644 index 000000000000..03de4239f204 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java @@ -0,0 +1,174 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.snapshot; + +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalLong; +import java.util.concurrent.TimeUnit; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * An SDK-internal implementation of {@link ProgressSnapshot}. + */ +@SdkInternalApi +public class DefaultProgressSnapshot + implements ToCopyableBuilder, + ProgressSnapshot { + + private final long transferredBytes; + private final Long totalBytes; + private final Optional startTime; + + public DefaultProgressSnapshot(Builder builder) { + if (builder.totalBytes != null) { + Validate.isNotNegative(builder.totalBytes, "totalBytes"); + Validate.isTrue(builder.transferredBytes <= builder.totalBytes, + "transferredBytes (%s) must not be greater than totalBytes (%s)", + builder.transferredBytes, builder.totalBytes); + } + Validate.paramNotNull(builder.transferredBytes, "byteTransferred"); + this.transferredBytes = Validate.isNotNegative(builder.transferredBytes, "transferredBytes"); + this.totalBytes = builder.totalBytes; + + if (builder.startTime.isPresent()) { + Instant currentTime = Instant.now(); + Validate.isTrue(currentTime.isAfter(builder.startTime.get()), + "currentTime (%s) must not be before startTime (%s)", + currentTime, builder.startTime.get()); + } + + this.startTime = builder.startTime; + } + + @Override + public long transferredBytes() { + return this.transferredBytes; + } + + @Override + public Optional startTime() { + return this.startTime; + } + + @Override + public Optional elapsedTime() { + return this.startTime.isPresent() ? Optional.of(Duration.between(startTime.get(), Instant.now())) : Optional.empty(); + } + + @Override + public Optional estimatedTimeRemaining() { + if (!elapsedTime().isPresent() || !remainingBytes().isPresent()) { + return Optional.empty(); + } + + long remainingTime = remainingBytes().getAsLong() * elapsedTime().get().toMillis() / transferredBytes; + return Optional.of(Duration.ofMillis(remainingTime)); + + } + + @Override + public OptionalDouble averageBytesPer(TimeUnit timeUnit) { + if (!this.elapsedTime().isPresent()) { + return OptionalDouble.empty(); + } + + return this.elapsedTime().get().equals(Duration.ZERO) ? OptionalDouble.of(1.0) : + OptionalDouble.of((double) this.transferredBytes / timeUnit.convert(elapsedTime().get().toMillis(), timeUnit)); + } + + @Override + public OptionalLong totalBytes() { + return totalBytes == null ? OptionalLong.empty() : OptionalLong.of(totalBytes); + } + + @Override + public OptionalDouble ratioTransferred() { + if (totalBytes == null) { + return OptionalDouble.empty(); + } + return totalBytes == 0 ? OptionalDouble.of(1.0) : OptionalDouble.of(transferredBytes / totalBytes.doubleValue()); + } + + @Override + public OptionalLong remainingBytes() { + if (totalBytes == null) { + return OptionalLong.empty(); + } + return totalBytes == 0 ? OptionalLong.of(0) : OptionalLong.of(totalBytes - transferredBytes); + } + + @Override + public DefaultProgressSnapshot.Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder implements CopyableBuilder { + private long transferredBytes; + private Long totalBytes; + private Optional startTime = Optional.empty(); + + private Builder() { + } + + private Builder(DefaultProgressSnapshot progressSnapshot) { + this.transferredBytes = progressSnapshot.transferredBytes; + this.totalBytes = progressSnapshot.totalBytes; + this.startTime = progressSnapshot.startTime; + } + + public Builder transferredBytes(Long transferredBytes) { + this.transferredBytes = transferredBytes; + return this; + } + + public Long getTransferredBytes() { + return this.transferredBytes; + } + + public Builder totalBytes(Long totalBytes) { + this.totalBytes = totalBytes; + return this; + } + + public Long getTotalBytes() { + return this.totalBytes; + } + + public Builder startTime(Instant startTime) { + this.startTime = Optional.of(startTime); + return this; + } + + public Optional startTime() { + return this.startTime; + } + + @Override + public DefaultProgressSnapshot build() { + return new DefaultProgressSnapshot(this); + } + } +} \ No newline at end of file diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java new file mode 100644 index 000000000000..2cf09d75cec5 --- /dev/null +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java @@ -0,0 +1,202 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.Assert.assertEquals; + +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; + +public class DefaultProgressSnapshotTest { + @Test + public void bytesTransferred_greaterThan_totalBytes_shouldThrow() { + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(1L); + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("transferredBytes (2) must not be greater than totalBytes (1)"); + } + + @Test + public void transferredBytes_negative_shouldThrow() { + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(-2L); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("transferredBytes must not be negative"); + } + + @Test + public void transferredBytes_null_isZero() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .build(); + + Assertions.assertEquals(0, snapshot.transferredBytes()); + } + + @Test + public void totalBytes_negative_shouldThrow() { + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(-2L); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("totalBytes must not be negative"); + } + + @Test + public void totalBytes_empty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .build(); + + assertThat(snapshot.totalBytes()).isNotPresent(); + } + + @Test + public void totalBytes() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(5L) + .build(); + + Assertions.assertEquals(5, snapshot.totalBytes().getAsLong()); + } + + @Test + public void startTime_after_currentTime_shouldThrow() { + Instant timeAfterFiveSeconds = Instant.now().plus(5, ChronoUnit.SECONDS); + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(0L) + .startTime(timeAfterFiveSeconds); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("currentTime") + .hasMessageEndingWith(" must not be before startTime (" + timeAfterFiveSeconds + ")"); + } + + @Test + public void ratioTransferred_withoutTotalBytes_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .build(); + assertThat(snapshot.ratioTransferred()).isNotPresent(); + } + + @Test + public void ratioTransferred() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + assertEquals(0.2, snapshot.ratioTransferred().getAsDouble(), 0.0); + } + + @Test + public void remainingBytes_withoutTotalBytes_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .build(); + assertThat(snapshot.remainingBytes()).isNotPresent(); + } + + @Test + public void remainingBytes() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + Assertions.assertEquals(4.0, snapshot.remainingBytes().getAsLong(), 0.0); + } + + @Test + public void elapsedTime_withoutStartTime_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .build(); + assertThat(snapshot.elapsedTime()).isNotPresent(); + } + + @Test + public void elapsedTime() { + + Instant startTime = Instant.now().minusMillis(100); + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(startTime) + .build(); + Duration expectedDuration = Duration.between(startTime, Instant.now()); + + Assertions.assertEquals(snapshot.elapsedTime().get().toMillis(), expectedDuration.toMillis(), 0.1); + } + + @Test + public void averageBytesPer_withoutStartTime_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + assertThat(snapshot.averageBytesPer(TimeUnit.MILLISECONDS)).isNotPresent(); + } + + @Test + public void averageBytesPer() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(100L) + .startTime(Instant.now().minusMillis(100)) + .build(); + Assertions.assertEquals(1.0, snapshot.averageBytesPer(TimeUnit.MILLISECONDS).getAsDouble(), 0.2); + } + + @Test + public void estimatedTimeRemaining_withoutStartTime_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); + } + + @Test + public void estimatedTimeRemaining_withoutTotalBytes_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(Instant.now().minusMillis(5)) + .build(); + assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); + } + + @Test + public void estimatedTimeRemaining() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(100L) + .startTime(Instant.now().minusSeconds(1)) + .totalBytes(500L) + .build(); + Assertions.assertEquals(4000.0, snapshot.estimatedTimeRemaining().get().toMillis(), 10.0); + } +} From 7b24093c0bd828730f94e04ec1c073784ddd53ec Mon Sep 17 00:00:00 2001 From: Krishnan Date: Thu, 14 Dec 2023 02:45:18 -0800 Subject: [PATCH 15/19] Revert "Implement Default Progress Snapshot" This reverts commit 276fb70834a56be09e5d0805b55545c85843f369. --- core/sdk-core/pom.xml | 8 - .../listener/DefaultSdkRequestProgress.java | 56 ----- .../snapshot/DefaultProgressSnapshot.java | 174 --------------- .../ListenerProgress.java} | 5 +- .../progress/DefaultProgressSnapshotTest.java | 202 ------------------ 5 files changed, 2 insertions(+), 443 deletions(-) delete mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java delete mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java rename core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/{listener/SdkRequestProgress.java => snapshot/ListenerProgress.java} (85%) delete mode 100644 core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java diff --git a/core/sdk-core/pom.xml b/core/sdk-core/pom.xml index d15be86a3608..f19120fde62d 100644 --- a/core/sdk-core/pom.xml +++ b/core/sdk-core/pom.xml @@ -284,14 +284,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java deleted file mode 100644 index 6c8a602afcc6..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 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 software.amazon.awssdk.core.progress.listener; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import software.amazon.awssdk.annotations.Mutable; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; -import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; - -/** - * An SDK-internal implementation of {@link SdkRequestProgress}. This implementation acts as a thin wrapper around {@link - * AtomicReference}, where calls to get the latest {@link #progressSnapshot()} simply return the latest reference, while {@link - * SdkRequestProgressUpdater} is responsible for continuously updating the latest reference. - * - * @see SdkRequestProgress - */ -@Mutable -@ThreadSafe -@SdkInternalApi -public class DefaultSdkRequestProgress implements SdkRequestProgress { - - private final AtomicReference snapshot; - - public DefaultSdkRequestProgress(ProgressSnapshot snapshot) { - this.snapshot = new AtomicReference<>(snapshot); - } - - /** - * Atomically convert the current snapshot reference to its {@link Builder}, perform updates using the provided {@link - * Consumer}, and save the result as the latest snapshot. - */ - public ProgressSnapshot updateAndGet(Consumer updater) { - return this.snapshot.updateAndGet(s -> ((DefaultProgressSnapshot) s).copy(updater)); - } - - @Override - public ProgressSnapshot progressSnapshot() { - return this.snapshot.get(); - } -} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java deleted file mode 100644 index 03de4239f204..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 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 software.amazon.awssdk.core.progress.snapshot; - -import java.time.Duration; -import java.time.Instant; -import java.util.Optional; -import java.util.OptionalDouble; -import java.util.OptionalLong; -import java.util.concurrent.TimeUnit; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.utils.Validate; -import software.amazon.awssdk.utils.builder.CopyableBuilder; -import software.amazon.awssdk.utils.builder.ToCopyableBuilder; - -/** - * An SDK-internal implementation of {@link ProgressSnapshot}. - */ -@SdkInternalApi -public class DefaultProgressSnapshot - implements ToCopyableBuilder, - ProgressSnapshot { - - private final long transferredBytes; - private final Long totalBytes; - private final Optional startTime; - - public DefaultProgressSnapshot(Builder builder) { - if (builder.totalBytes != null) { - Validate.isNotNegative(builder.totalBytes, "totalBytes"); - Validate.isTrue(builder.transferredBytes <= builder.totalBytes, - "transferredBytes (%s) must not be greater than totalBytes (%s)", - builder.transferredBytes, builder.totalBytes); - } - Validate.paramNotNull(builder.transferredBytes, "byteTransferred"); - this.transferredBytes = Validate.isNotNegative(builder.transferredBytes, "transferredBytes"); - this.totalBytes = builder.totalBytes; - - if (builder.startTime.isPresent()) { - Instant currentTime = Instant.now(); - Validate.isTrue(currentTime.isAfter(builder.startTime.get()), - "currentTime (%s) must not be before startTime (%s)", - currentTime, builder.startTime.get()); - } - - this.startTime = builder.startTime; - } - - @Override - public long transferredBytes() { - return this.transferredBytes; - } - - @Override - public Optional startTime() { - return this.startTime; - } - - @Override - public Optional elapsedTime() { - return this.startTime.isPresent() ? Optional.of(Duration.between(startTime.get(), Instant.now())) : Optional.empty(); - } - - @Override - public Optional estimatedTimeRemaining() { - if (!elapsedTime().isPresent() || !remainingBytes().isPresent()) { - return Optional.empty(); - } - - long remainingTime = remainingBytes().getAsLong() * elapsedTime().get().toMillis() / transferredBytes; - return Optional.of(Duration.ofMillis(remainingTime)); - - } - - @Override - public OptionalDouble averageBytesPer(TimeUnit timeUnit) { - if (!this.elapsedTime().isPresent()) { - return OptionalDouble.empty(); - } - - return this.elapsedTime().get().equals(Duration.ZERO) ? OptionalDouble.of(1.0) : - OptionalDouble.of((double) this.transferredBytes / timeUnit.convert(elapsedTime().get().toMillis(), timeUnit)); - } - - @Override - public OptionalLong totalBytes() { - return totalBytes == null ? OptionalLong.empty() : OptionalLong.of(totalBytes); - } - - @Override - public OptionalDouble ratioTransferred() { - if (totalBytes == null) { - return OptionalDouble.empty(); - } - return totalBytes == 0 ? OptionalDouble.of(1.0) : OptionalDouble.of(transferredBytes / totalBytes.doubleValue()); - } - - @Override - public OptionalLong remainingBytes() { - if (totalBytes == null) { - return OptionalLong.empty(); - } - return totalBytes == 0 ? OptionalLong.of(0) : OptionalLong.of(totalBytes - transferredBytes); - } - - @Override - public DefaultProgressSnapshot.Builder toBuilder() { - return new Builder(this); - } - - public static Builder builder() { - return new Builder(); - } - - public static final class Builder implements CopyableBuilder { - private long transferredBytes; - private Long totalBytes; - private Optional startTime = Optional.empty(); - - private Builder() { - } - - private Builder(DefaultProgressSnapshot progressSnapshot) { - this.transferredBytes = progressSnapshot.transferredBytes; - this.totalBytes = progressSnapshot.totalBytes; - this.startTime = progressSnapshot.startTime; - } - - public Builder transferredBytes(Long transferredBytes) { - this.transferredBytes = transferredBytes; - return this; - } - - public Long getTransferredBytes() { - return this.transferredBytes; - } - - public Builder totalBytes(Long totalBytes) { - this.totalBytes = totalBytes; - return this; - } - - public Long getTotalBytes() { - return this.totalBytes; - } - - public Builder startTime(Instant startTime) { - this.startTime = Optional.of(startTime); - return this; - } - - public Optional startTime() { - return this.startTime; - } - - @Override - public DefaultProgressSnapshot build() { - return new DefaultProgressSnapshot(this); - } - } -} \ No newline at end of file diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java similarity index 85% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java index 837b30535868..71439266246f 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java @@ -13,17 +13,16 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.listener; +package software.amazon.awssdk.core.progress.snapshot; import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; @Immutable @ThreadSafe @SdkPublicApi -public interface SdkRequestProgress { +public interface ListenerProgress { /** * Takes a snapshot of the request execution progress diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java deleted file mode 100644 index 2cf09d75cec5..000000000000 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 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 software.amazon.awssdk.core.progress; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.junit.Assert.assertEquals; - -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; - -public class DefaultProgressSnapshotTest { - @Test - public void bytesTransferred_greaterThan_totalBytes_shouldThrow() { - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .totalBytes(1L); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("transferredBytes (2) must not be greater than totalBytes (1)"); - } - - @Test - public void transferredBytes_negative_shouldThrow() { - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(-2L); - - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("transferredBytes must not be negative"); - } - - @Test - public void transferredBytes_null_isZero() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .build(); - - Assertions.assertEquals(0, snapshot.transferredBytes()); - } - - @Test - public void totalBytes_negative_shouldThrow() { - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .totalBytes(-2L); - - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("totalBytes must not be negative"); - } - - @Test - public void totalBytes_empty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .build(); - - assertThat(snapshot.totalBytes()).isNotPresent(); - } - - @Test - public void totalBytes() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .totalBytes(5L) - .build(); - - Assertions.assertEquals(5, snapshot.totalBytes().getAsLong()); - } - - @Test - public void startTime_after_currentTime_shouldThrow() { - Instant timeAfterFiveSeconds = Instant.now().plus(5, ChronoUnit.SECONDS); - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(0L) - .startTime(timeAfterFiveSeconds); - - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("currentTime") - .hasMessageEndingWith(" must not be before startTime (" + timeAfterFiveSeconds + ")"); - } - - @Test - public void ratioTransferred_withoutTotalBytes_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .build(); - assertThat(snapshot.ratioTransferred()).isNotPresent(); - } - - @Test - public void ratioTransferred() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - assertEquals(0.2, snapshot.ratioTransferred().getAsDouble(), 0.0); - } - - @Test - public void remainingBytes_withoutTotalBytes_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .build(); - assertThat(snapshot.remainingBytes()).isNotPresent(); - } - - @Test - public void remainingBytes() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - Assertions.assertEquals(4.0, snapshot.remainingBytes().getAsLong(), 0.0); - } - - @Test - public void elapsedTime_withoutStartTime_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .build(); - assertThat(snapshot.elapsedTime()).isNotPresent(); - } - - @Test - public void elapsedTime() { - - Instant startTime = Instant.now().minusMillis(100); - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .startTime(startTime) - .build(); - Duration expectedDuration = Duration.between(startTime, Instant.now()); - - Assertions.assertEquals(snapshot.elapsedTime().get().toMillis(), expectedDuration.toMillis(), 0.1); - } - - @Test - public void averageBytesPer_withoutStartTime_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - assertThat(snapshot.averageBytesPer(TimeUnit.MILLISECONDS)).isNotPresent(); - } - - @Test - public void averageBytesPer() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(100L) - .startTime(Instant.now().minusMillis(100)) - .build(); - Assertions.assertEquals(1.0, snapshot.averageBytesPer(TimeUnit.MILLISECONDS).getAsDouble(), 0.2); - } - - @Test - public void estimatedTimeRemaining_withoutStartTime_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); - } - - @Test - public void estimatedTimeRemaining_withoutTotalBytes_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .startTime(Instant.now().minusMillis(5)) - .build(); - assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); - } - - @Test - public void estimatedTimeRemaining() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(100L) - .startTime(Instant.now().minusSeconds(1)) - .totalBytes(500L) - .build(); - Assertions.assertEquals(4000.0, snapshot.estimatedTimeRemaining().get().toMillis(), 10.0); - } -} From a943002fde07c59fdc227030168c77deae9d7e98 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Thu, 14 Dec 2023 02:46:36 -0800 Subject: [PATCH 16/19] Revert "Revert "Implement Default Progress Snapshot"" This reverts commit 7b24093c0bd828730f94e04ec1c073784ddd53ec. --- core/sdk-core/pom.xml | 8 + .../listener/DefaultSdkRequestProgress.java | 56 +++++ .../SdkRequestProgress.java} | 5 +- .../snapshot/DefaultProgressSnapshot.java | 174 +++++++++++++++ .../progress/DefaultProgressSnapshotTest.java | 202 ++++++++++++++++++ 5 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java rename core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/{snapshot/ListenerProgress.java => listener/SdkRequestProgress.java} (85%) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java create mode 100644 core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java diff --git a/core/sdk-core/pom.xml b/core/sdk-core/pom.xml index f19120fde62d..d15be86a3608 100644 --- a/core/sdk-core/pom.xml +++ b/core/sdk-core/pom.xml @@ -284,6 +284,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java new file mode 100644 index 000000000000..6c8a602afcc6 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java @@ -0,0 +1,56 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.listener; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Mutable; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; +import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; + +/** + * An SDK-internal implementation of {@link SdkRequestProgress}. This implementation acts as a thin wrapper around {@link + * AtomicReference}, where calls to get the latest {@link #progressSnapshot()} simply return the latest reference, while {@link + * SdkRequestProgressUpdater} is responsible for continuously updating the latest reference. + * + * @see SdkRequestProgress + */ +@Mutable +@ThreadSafe +@SdkInternalApi +public class DefaultSdkRequestProgress implements SdkRequestProgress { + + private final AtomicReference snapshot; + + public DefaultSdkRequestProgress(ProgressSnapshot snapshot) { + this.snapshot = new AtomicReference<>(snapshot); + } + + /** + * Atomically convert the current snapshot reference to its {@link Builder}, perform updates using the provided {@link + * Consumer}, and save the result as the latest snapshot. + */ + public ProgressSnapshot updateAndGet(Consumer updater) { + return this.snapshot.updateAndGet(s -> ((DefaultProgressSnapshot) s).copy(updater)); + } + + @Override + public ProgressSnapshot progressSnapshot() { + return this.snapshot.get(); + } +} diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java similarity index 85% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java index 71439266246f..837b30535868 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ListenerProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/SdkRequestProgress.java @@ -13,16 +13,17 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.snapshot; +package software.amazon.awssdk.core.progress.listener; import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; @Immutable @ThreadSafe @SdkPublicApi -public interface ListenerProgress { +public interface SdkRequestProgress { /** * Takes a snapshot of the request execution progress diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java new file mode 100644 index 000000000000..03de4239f204 --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java @@ -0,0 +1,174 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress.snapshot; + +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalLong; +import java.util.concurrent.TimeUnit; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * An SDK-internal implementation of {@link ProgressSnapshot}. + */ +@SdkInternalApi +public class DefaultProgressSnapshot + implements ToCopyableBuilder, + ProgressSnapshot { + + private final long transferredBytes; + private final Long totalBytes; + private final Optional startTime; + + public DefaultProgressSnapshot(Builder builder) { + if (builder.totalBytes != null) { + Validate.isNotNegative(builder.totalBytes, "totalBytes"); + Validate.isTrue(builder.transferredBytes <= builder.totalBytes, + "transferredBytes (%s) must not be greater than totalBytes (%s)", + builder.transferredBytes, builder.totalBytes); + } + Validate.paramNotNull(builder.transferredBytes, "byteTransferred"); + this.transferredBytes = Validate.isNotNegative(builder.transferredBytes, "transferredBytes"); + this.totalBytes = builder.totalBytes; + + if (builder.startTime.isPresent()) { + Instant currentTime = Instant.now(); + Validate.isTrue(currentTime.isAfter(builder.startTime.get()), + "currentTime (%s) must not be before startTime (%s)", + currentTime, builder.startTime.get()); + } + + this.startTime = builder.startTime; + } + + @Override + public long transferredBytes() { + return this.transferredBytes; + } + + @Override + public Optional startTime() { + return this.startTime; + } + + @Override + public Optional elapsedTime() { + return this.startTime.isPresent() ? Optional.of(Duration.between(startTime.get(), Instant.now())) : Optional.empty(); + } + + @Override + public Optional estimatedTimeRemaining() { + if (!elapsedTime().isPresent() || !remainingBytes().isPresent()) { + return Optional.empty(); + } + + long remainingTime = remainingBytes().getAsLong() * elapsedTime().get().toMillis() / transferredBytes; + return Optional.of(Duration.ofMillis(remainingTime)); + + } + + @Override + public OptionalDouble averageBytesPer(TimeUnit timeUnit) { + if (!this.elapsedTime().isPresent()) { + return OptionalDouble.empty(); + } + + return this.elapsedTime().get().equals(Duration.ZERO) ? OptionalDouble.of(1.0) : + OptionalDouble.of((double) this.transferredBytes / timeUnit.convert(elapsedTime().get().toMillis(), timeUnit)); + } + + @Override + public OptionalLong totalBytes() { + return totalBytes == null ? OptionalLong.empty() : OptionalLong.of(totalBytes); + } + + @Override + public OptionalDouble ratioTransferred() { + if (totalBytes == null) { + return OptionalDouble.empty(); + } + return totalBytes == 0 ? OptionalDouble.of(1.0) : OptionalDouble.of(transferredBytes / totalBytes.doubleValue()); + } + + @Override + public OptionalLong remainingBytes() { + if (totalBytes == null) { + return OptionalLong.empty(); + } + return totalBytes == 0 ? OptionalLong.of(0) : OptionalLong.of(totalBytes - transferredBytes); + } + + @Override + public DefaultProgressSnapshot.Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder implements CopyableBuilder { + private long transferredBytes; + private Long totalBytes; + private Optional startTime = Optional.empty(); + + private Builder() { + } + + private Builder(DefaultProgressSnapshot progressSnapshot) { + this.transferredBytes = progressSnapshot.transferredBytes; + this.totalBytes = progressSnapshot.totalBytes; + this.startTime = progressSnapshot.startTime; + } + + public Builder transferredBytes(Long transferredBytes) { + this.transferredBytes = transferredBytes; + return this; + } + + public Long getTransferredBytes() { + return this.transferredBytes; + } + + public Builder totalBytes(Long totalBytes) { + this.totalBytes = totalBytes; + return this; + } + + public Long getTotalBytes() { + return this.totalBytes; + } + + public Builder startTime(Instant startTime) { + this.startTime = Optional.of(startTime); + return this; + } + + public Optional startTime() { + return this.startTime; + } + + @Override + public DefaultProgressSnapshot build() { + return new DefaultProgressSnapshot(this); + } + } +} \ No newline at end of file diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java new file mode 100644 index 000000000000..2cf09d75cec5 --- /dev/null +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java @@ -0,0 +1,202 @@ +/* + * Copyright 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 software.amazon.awssdk.core.progress; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.Assert.assertEquals; + +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; + +public class DefaultProgressSnapshotTest { + @Test + public void bytesTransferred_greaterThan_totalBytes_shouldThrow() { + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(1L); + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("transferredBytes (2) must not be greater than totalBytes (1)"); + } + + @Test + public void transferredBytes_negative_shouldThrow() { + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(-2L); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("transferredBytes must not be negative"); + } + + @Test + public void transferredBytes_null_isZero() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .build(); + + Assertions.assertEquals(0, snapshot.transferredBytes()); + } + + @Test + public void totalBytes_negative_shouldThrow() { + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(-2L); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("totalBytes must not be negative"); + } + + @Test + public void totalBytes_empty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .build(); + + assertThat(snapshot.totalBytes()).isNotPresent(); + } + + @Test + public void totalBytes() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(5L) + .build(); + + Assertions.assertEquals(5, snapshot.totalBytes().getAsLong()); + } + + @Test + public void startTime_after_currentTime_shouldThrow() { + Instant timeAfterFiveSeconds = Instant.now().plus(5, ChronoUnit.SECONDS); + DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() + .transferredBytes(0L) + .startTime(timeAfterFiveSeconds); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("currentTime") + .hasMessageEndingWith(" must not be before startTime (" + timeAfterFiveSeconds + ")"); + } + + @Test + public void ratioTransferred_withoutTotalBytes_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .build(); + assertThat(snapshot.ratioTransferred()).isNotPresent(); + } + + @Test + public void ratioTransferred() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + assertEquals(0.2, snapshot.ratioTransferred().getAsDouble(), 0.0); + } + + @Test + public void remainingBytes_withoutTotalBytes_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .build(); + assertThat(snapshot.remainingBytes()).isNotPresent(); + } + + @Test + public void remainingBytes() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + Assertions.assertEquals(4.0, snapshot.remainingBytes().getAsLong(), 0.0); + } + + @Test + public void elapsedTime_withoutStartTime_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .build(); + assertThat(snapshot.elapsedTime()).isNotPresent(); + } + + @Test + public void elapsedTime() { + + Instant startTime = Instant.now().minusMillis(100); + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(startTime) + .build(); + Duration expectedDuration = Duration.between(startTime, Instant.now()); + + Assertions.assertEquals(snapshot.elapsedTime().get().toMillis(), expectedDuration.toMillis(), 0.1); + } + + @Test + public void averageBytesPer_withoutStartTime_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + assertThat(snapshot.averageBytesPer(TimeUnit.MILLISECONDS)).isNotPresent(); + } + + @Test + public void averageBytesPer() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(100L) + .startTime(Instant.now().minusMillis(100)) + .build(); + Assertions.assertEquals(1.0, snapshot.averageBytesPer(TimeUnit.MILLISECONDS).getAsDouble(), 0.2); + } + + @Test + public void estimatedTimeRemaining_withoutStartTime_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .totalBytes(5L) + .build(); + assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); + } + + @Test + public void estimatedTimeRemaining_withoutTotalBytes_isEmpty() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(Instant.now().minusMillis(5)) + .build(); + assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); + } + + @Test + public void estimatedTimeRemaining() { + DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() + .transferredBytes(100L) + .startTime(Instant.now().minusSeconds(1)) + .totalBytes(500L) + .build(); + Assertions.assertEquals(4000.0, snapshot.estimatedTimeRemaining().get().toMillis(), 10.0); + } +} From 1d56ce252d24dc397b8f03c718dbaf4f055b2525 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Thu, 14 Dec 2023 02:50:20 -0800 Subject: [PATCH 17/19] Remove changes to pom --- core/sdk-core/pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/sdk-core/pom.xml b/core/sdk-core/pom.xml index d15be86a3608..f19120fde62d 100644 --- a/core/sdk-core/pom.xml +++ b/core/sdk-core/pom.xml @@ -284,14 +284,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - From 2e66a9a9438e8313af2b3160578f5f5bf28190d6 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Wed, 20 Dec 2023 11:49:14 -0800 Subject: [PATCH 18/19] Add parameterized unit tests --- .../listener/DefaultSdkRequestProgress.java | 5 +- .../snapshot/DefaultProgressSnapshot.java | 89 ++++--- .../progress/snapshot/ProgressSnapshot.java | 6 +- .../progress/DefaultProgressSnapshotTest.java | 238 ++++++++---------- .../DefaultTransferProgressSnapshot.java | 8 - 5 files changed, 159 insertions(+), 187 deletions(-) rename core/sdk-core/src/main/java/software/amazon/awssdk/core/{progress => internal}/listener/DefaultSdkRequestProgress.java (91%) rename core/sdk-core/src/main/java/software/amazon/awssdk/core/{progress => internal}/snapshot/DefaultProgressSnapshot.java (68%) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/listener/DefaultSdkRequestProgress.java similarity index 91% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/listener/DefaultSdkRequestProgress.java index 6c8a602afcc6..20a4b7b3e2fb 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/listener/DefaultSdkRequestProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/listener/DefaultSdkRequestProgress.java @@ -13,14 +13,15 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.listener; +package software.amazon.awssdk.core.internal.listener; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Mutable; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; +import software.amazon.awssdk.core.internal.snapshot.DefaultProgressSnapshot; +import software.amazon.awssdk.core.progress.listener.SdkRequestProgress; import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; /** diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/snapshot/DefaultProgressSnapshot.java similarity index 68% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/snapshot/DefaultProgressSnapshot.java index 03de4239f204..2f20b450cf25 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/DefaultProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/snapshot/DefaultProgressSnapshot.java @@ -13,15 +13,18 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.progress.snapshot; +package software.amazon.awssdk.core.internal.snapshot; import java.time.Duration; import java.time.Instant; +import java.util.Objects; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalLong; import java.util.concurrent.TimeUnit; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; +import software.amazon.awssdk.utils.ToString; import software.amazon.awssdk.utils.Validate; import software.amazon.awssdk.utils.builder.CopyableBuilder; import software.amazon.awssdk.utils.builder.ToCopyableBuilder; @@ -36,7 +39,7 @@ public class DefaultProgressSnapshot private final long transferredBytes; private final Long totalBytes; - private final Optional startTime; + private final Instant startTime; public DefaultProgressSnapshot(Builder builder) { if (builder.totalBytes != null) { @@ -45,17 +48,11 @@ public DefaultProgressSnapshot(Builder builder) { "transferredBytes (%s) must not be greater than totalBytes (%s)", builder.transferredBytes, builder.totalBytes); } - Validate.paramNotNull(builder.transferredBytes, "byteTransferred"); + Validate.paramNotNull(builder.transferredBytes, "transferredBytes"); this.transferredBytes = Validate.isNotNegative(builder.transferredBytes, "transferredBytes"); this.totalBytes = builder.totalBytes; - if (builder.startTime.isPresent()) { - Instant currentTime = Instant.now(); - Validate.isTrue(currentTime.isAfter(builder.startTime.get()), - "currentTime (%s) must not be before startTime (%s)", - currentTime, builder.startTime.get()); - } - + Validate.paramNotNull(builder.startTime, "startTime"); this.startTime = builder.startTime; } @@ -65,34 +62,30 @@ public long transferredBytes() { } @Override - public Optional startTime() { + public Instant startTime() { return this.startTime; } @Override - public Optional elapsedTime() { - return this.startTime.isPresent() ? Optional.of(Duration.between(startTime.get(), Instant.now())) : Optional.empty(); + public Duration elapsedTime() { + return Duration.between(this.startTime, Instant.now()); } @Override public Optional estimatedTimeRemaining() { - if (!elapsedTime().isPresent() || !remainingBytes().isPresent()) { + if (!remainingBytes().isPresent()) { return Optional.empty(); } - long remainingTime = remainingBytes().getAsLong() * elapsedTime().get().toMillis() / transferredBytes; + long remainingTime = remainingBytes().getAsLong() * elapsedTime().toMillis() / transferredBytes; return Optional.of(Duration.ofMillis(remainingTime)); } @Override - public OptionalDouble averageBytesPer(TimeUnit timeUnit) { - if (!this.elapsedTime().isPresent()) { - return OptionalDouble.empty(); - } - - return this.elapsedTime().get().equals(Duration.ZERO) ? OptionalDouble.of(1.0) : - OptionalDouble.of((double) this.transferredBytes / timeUnit.convert(elapsedTime().get().toMillis(), timeUnit)); + public double averageBytesPer(TimeUnit timeUnit) { + return this.elapsedTime().equals(Duration.ZERO) ? 1.0 : + (double) this.transferredBytes / timeUnit.convert(elapsedTime().toMillis(), timeUnit); } @Override @@ -116,6 +109,42 @@ public OptionalLong remainingBytes() { return totalBytes == 0 ? OptionalLong.of(0) : OptionalLong.of(totalBytes - transferredBytes); } + @Override + public String toString() { + return ToString.builder("ProgressSnapshot") + .add("transferredBytes", transferredBytes) + .add("totalBytes", totalBytes) + .add("elapsedTime", this.elapsedTime()) + .build(); + } + + @Override + public int hashCode() { + int result = (int) (transferredBytes ^ (transferredBytes >>> 32)); + result = 31 * result + (totalBytes != null ? totalBytes.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DefaultProgressSnapshot that = (DefaultProgressSnapshot) o; + + if (this.transferredBytes != that.transferredBytes) { + return false; + } + if (!Objects.equals(this.totalBytes, that.totalBytes)) { + return false; + } + return Objects.equals(this.startTime, that.startTime); + } + @Override public DefaultProgressSnapshot.Builder toBuilder() { return new Builder(this); @@ -128,7 +157,7 @@ public static Builder builder() { public static final class Builder implements CopyableBuilder { private long transferredBytes; private Long totalBytes; - private Optional startTime = Optional.empty(); + private Instant startTime; private Builder() { } @@ -144,28 +173,16 @@ public Builder transferredBytes(Long transferredBytes) { return this; } - public Long getTransferredBytes() { - return this.transferredBytes; - } - public Builder totalBytes(Long totalBytes) { this.totalBytes = totalBytes; return this; } - public Long getTotalBytes() { - return this.totalBytes; - } - public Builder startTime(Instant startTime) { - this.startTime = Optional.of(startTime); + this.startTime = startTime; return this; } - public Optional startTime() { - return this.startTime; - } - @Override public DefaultProgressSnapshot build() { return new DefaultProgressSnapshot(this); diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java index 829ce35b0dc0..300657e79399 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/progress/snapshot/ProgressSnapshot.java @@ -37,12 +37,12 @@ public interface ProgressSnapshot { /** * Time at which the HTTP Request header is sent */ - Optional startTime(); + Instant startTime(); /** * Elapsed time since the HTTP request header was sent to the service */ - Optional elapsedTime(); + Duration elapsedTime(); /** * If transaction size is known, estimate time remaining for transaction completion @@ -57,7 +57,7 @@ public interface ProgressSnapshot { /** * Rate of transfer */ - OptionalDouble averageBytesPer(TimeUnit timeUnit); + double averageBytesPer(TimeUnit timeUnit); /** * The total size of the transfer, in bytes, or {@link Optional#empty()} if total payload being transacted is unknown diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java index 2cf09d75cec5..fdf159727eaf 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java @@ -18,93 +18,117 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import software.amazon.awssdk.core.progress.snapshot.DefaultProgressSnapshot; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import software.amazon.awssdk.core.internal.snapshot.DefaultProgressSnapshot; public class DefaultProgressSnapshotTest { - @Test - public void bytesTransferred_greaterThan_totalBytes_shouldThrow() { - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .totalBytes(1L); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("transferredBytes (2) must not be greater than totalBytes (1)"); - } - @Test - public void transferredBytes_negative_shouldThrow() { - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(-2L); - - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("transferredBytes must not be negative"); - } - - @Test - public void transferredBytes_null_isZero() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .build(); + private static Stream getArgumentsForInvalidParameterValidationTests() { + return Stream.of(Arguments.of("transferredBytes (2) must not be greater than totalBytes (1)", + DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(1L), + new IllegalArgumentException()), + Arguments.of("startTime must not be null.", + DefaultProgressSnapshot.builder() + .transferredBytes(2L), + new NullPointerException()), + Arguments.of("transferredBytes must not be negative", + DefaultProgressSnapshot.builder() + .transferredBytes(-2L), + new IllegalArgumentException()), + Arguments.of("totalBytes must not be negative", + DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(-2L), + new IllegalArgumentException())); + } + + private static Stream getArgumentsForMissingParameterValidationTests() { + + DefaultProgressSnapshot snapshotNoTotalBytes = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .startTime(Instant.now()) + .build(); + + DefaultProgressSnapshot snapshotRatioTransferredWithoutTotalBytesIsEmpty = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(Instant.now()) + .build(); + + DefaultProgressSnapshot snapshotRemainingBytesWithoutTotalBytesIsEmpty = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(Instant.now()) + .build(); + + DefaultProgressSnapshot snapshotEstimatedTimeRemainingWithoutTotalBytesIsEmpty = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(Instant.now()) + .build(); + + return Stream.of(Arguments.of(snapshotNoTotalBytes.totalBytes().isPresent()), + Arguments.of(snapshotRatioTransferredWithoutTotalBytesIsEmpty.ratioTransferred().isPresent()), + Arguments.of(snapshotRemainingBytesWithoutTotalBytesIsEmpty.remainingBytes().isPresent()), + Arguments.of(snapshotEstimatedTimeRemainingWithoutTotalBytesIsEmpty.estimatedTimeRemaining().isPresent())); + } + + private static Stream getArgumentsForTimeTest() { + + DefaultProgressSnapshot snapshotEstimatedTimeReamining = DefaultProgressSnapshot.builder() + .transferredBytes(100L) + .startTime(Instant.now().minusSeconds(1)) + .totalBytes(500L) + .build(); - Assertions.assertEquals(0, snapshot.transferredBytes()); - } - - @Test - public void totalBytes_negative_shouldThrow() { - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .totalBytes(-2L); + Instant startTime = Instant.now().minusMillis(100); + DefaultProgressSnapshot snapshotTimeElapsed = DefaultProgressSnapshot.builder() + .transferredBytes(1L) + .startTime(startTime) + .build(); + Duration expectedDuration = Duration.between(startTime, Instant.now()); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("totalBytes must not be negative"); + return Stream.of(Arguments.of(4000L, snapshotEstimatedTimeReamining.estimatedTimeRemaining().get().toMillis() + , 10L), + Arguments.of(snapshotTimeElapsed.elapsedTime().toMillis(), expectedDuration.toMillis(), 1L)); } - @Test - public void totalBytes_empty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .build(); - - assertThat(snapshot.totalBytes()).isNotPresent(); - } + private static Stream getArgumentsForBytesTest() { - @Test - public void totalBytes() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(2L) - .totalBytes(5L) - .build(); + DefaultProgressSnapshot snapshotBytes = DefaultProgressSnapshot.builder() + .transferredBytes(2L) + .totalBytes(5L) + .startTime(Instant.now()) + .build(); - Assertions.assertEquals(5, snapshot.totalBytes().getAsLong()); + return Stream.of(Arguments.of(5L, snapshotBytes.totalBytes().getAsLong()), + Arguments.of(3L, snapshotBytes.remainingBytes().getAsLong(), 3L)); } - @Test - public void startTime_after_currentTime_shouldThrow() { - Instant timeAfterFiveSeconds = Instant.now().plus(5, ChronoUnit.SECONDS); - DefaultProgressSnapshot.Builder builder = DefaultProgressSnapshot.builder() - .transferredBytes(0L) - .startTime(timeAfterFiveSeconds); - + @ParameterizedTest + @MethodSource("getArgumentsForInvalidParameterValidationTests") + public void test_invalid_arguments_shouldThrow(String expectedErrorMsg, DefaultProgressSnapshot.Builder builder, + Exception e) { assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("currentTime") - .hasMessageEndingWith(" must not be before startTime (" + timeAfterFiveSeconds + ")"); + .isInstanceOf(e.getClass()) + .hasMessage(expectedErrorMsg); } - @Test - public void ratioTransferred_withoutTotalBytes_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .build(); - assertThat(snapshot.ratioTransferred()).isNotPresent(); + @ParameterizedTest + @MethodSource("getArgumentsForMissingParameterValidationTests") + public void test_missing_params_shouldReturnEmpty(boolean condition) { + Assertions.assertFalse(condition); } @Test @@ -112,55 +136,21 @@ public void ratioTransferred() { DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() .transferredBytes(1L) .totalBytes(5L) + .startTime(Instant.now()) .build(); assertEquals(0.2, snapshot.ratioTransferred().getAsDouble(), 0.0); } - @Test - public void remainingBytes_withoutTotalBytes_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .build(); - assertThat(snapshot.remainingBytes()).isNotPresent(); + @ParameterizedTest + @MethodSource("getArgumentsForBytesTest") + public void test_estimatedBytesRemaining_and_totalBytes(long expectedBytes, long actualBytes) { + Assertions.assertEquals(expectedBytes, actualBytes); } - @Test - public void remainingBytes() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - Assertions.assertEquals(4.0, snapshot.remainingBytes().getAsLong(), 0.0); - } - - @Test - public void elapsedTime_withoutStartTime_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .build(); - assertThat(snapshot.elapsedTime()).isNotPresent(); - } - - @Test - public void elapsedTime() { - - Instant startTime = Instant.now().minusMillis(100); - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .startTime(startTime) - .build(); - Duration expectedDuration = Duration.between(startTime, Instant.now()); - - Assertions.assertEquals(snapshot.elapsedTime().get().toMillis(), expectedDuration.toMillis(), 0.1); - } - - @Test - public void averageBytesPer_withoutStartTime_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - assertThat(snapshot.averageBytesPer(TimeUnit.MILLISECONDS)).isNotPresent(); + @ParameterizedTest + @MethodSource("getArgumentsForTimeTest") + public void test_elapsedTime_and_estimatedTimeRemaining(long expected, long timeInMillis, long delta) { + Assertions.assertEquals(expected, timeInMillis, delta); } @Test @@ -169,34 +159,6 @@ public void averageBytesPer() { .transferredBytes(100L) .startTime(Instant.now().minusMillis(100)) .build(); - Assertions.assertEquals(1.0, snapshot.averageBytesPer(TimeUnit.MILLISECONDS).getAsDouble(), 0.2); - } - - @Test - public void estimatedTimeRemaining_withoutStartTime_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .totalBytes(5L) - .build(); - assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); - } - - @Test - public void estimatedTimeRemaining_withoutTotalBytes_isEmpty() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(1L) - .startTime(Instant.now().minusMillis(5)) - .build(); - assertThat(snapshot.estimatedTimeRemaining()).isNotPresent(); - } - - @Test - public void estimatedTimeRemaining() { - DefaultProgressSnapshot snapshot = DefaultProgressSnapshot.builder() - .transferredBytes(100L) - .startTime(Instant.now().minusSeconds(1)) - .totalBytes(500L) - .build(); - Assertions.assertEquals(4000.0, snapshot.estimatedTimeRemaining().get().toMillis(), 10.0); + Assertions.assertEquals(1.0, snapshot.averageBytesPer(TimeUnit.MILLISECONDS), 0.2); } } diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java index b0bf72e91404..64b67b081ee9 100644 --- a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java @@ -150,19 +150,11 @@ public Builder transferredBytes(Long transferredBytes) { return this; } - public long getTransferredBytes() { - return transferredBytes; - } - public Builder totalBytes(Long totalBytes) { this.totalBytes = totalBytes; return this; } - public Long getTotalBytes() { - return totalBytes; - } - public Builder sdkResponse(SdkResponse sdkResponse) { this.sdkResponse = sdkResponse; return this; From 3ffbca2c98def6d09cdde06df9b1d15cdc0ce343 Mon Sep 17 00:00:00 2001 From: Krishnan Date: Wed, 20 Dec 2023 13:58:46 -0800 Subject: [PATCH 19/19] fix failing tests --- .../listener/DefaultSdkRequestProgress.java | 4 ++-- .../{ => progress}/snapshot/DefaultProgressSnapshot.java | 2 +- .../awssdk/core/progress/DefaultProgressSnapshotTest.java | 4 +--- .../progress/DefaultTransferProgressSnapshot.java | 8 ++++++++ 4 files changed, 12 insertions(+), 6 deletions(-) rename core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/{ => progress}/listener/DefaultSdkRequestProgress.java (93%) rename core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/{ => progress}/snapshot/DefaultProgressSnapshot.java (98%) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/listener/DefaultSdkRequestProgress.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/progress/listener/DefaultSdkRequestProgress.java similarity index 93% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/listener/DefaultSdkRequestProgress.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/progress/listener/DefaultSdkRequestProgress.java index 20a4b7b3e2fb..019c869ae060 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/listener/DefaultSdkRequestProgress.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/progress/listener/DefaultSdkRequestProgress.java @@ -13,14 +13,14 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.internal.listener; +package software.amazon.awssdk.core.internal.progress.listener; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Mutable; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.core.internal.snapshot.DefaultProgressSnapshot; +import software.amazon.awssdk.core.internal.progress.snapshot.DefaultProgressSnapshot; import software.amazon.awssdk.core.progress.listener.SdkRequestProgress; import software.amazon.awssdk.core.progress.snapshot.ProgressSnapshot; diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/snapshot/DefaultProgressSnapshot.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/progress/snapshot/DefaultProgressSnapshot.java similarity index 98% rename from core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/snapshot/DefaultProgressSnapshot.java rename to core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/progress/snapshot/DefaultProgressSnapshot.java index 2f20b450cf25..86e9a64004d2 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/snapshot/DefaultProgressSnapshot.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/progress/snapshot/DefaultProgressSnapshot.java @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -package software.amazon.awssdk.core.internal.snapshot; +package software.amazon.awssdk.core.internal.progress.snapshot; import java.time.Duration; import java.time.Instant; diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java index fdf159727eaf..760e7a0c3c90 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/progress/DefaultProgressSnapshotTest.java @@ -22,7 +22,6 @@ import java.time.Duration; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; @@ -30,8 +29,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; -import software.amazon.awssdk.core.internal.snapshot.DefaultProgressSnapshot; +import software.amazon.awssdk.core.internal.progress.snapshot.DefaultProgressSnapshot; public class DefaultProgressSnapshotTest { diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java index 64b67b081ee9..b0bf72e91404 100644 --- a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/DefaultTransferProgressSnapshot.java @@ -150,11 +150,19 @@ public Builder transferredBytes(Long transferredBytes) { return this; } + public long getTransferredBytes() { + return transferredBytes; + } + public Builder totalBytes(Long totalBytes) { this.totalBytes = totalBytes; return this; } + public Long getTotalBytes() { + return totalBytes; + } + public Builder sdkResponse(SdkResponse sdkResponse) { this.sdkResponse = sdkResponse; return this;