Skip to content

Commit da53f77

Browse files
Add support for S3TransferManager TransferListeners (#2770)
Add support for S3TransferManager TransferListeners This adds initial support for S3TransferManager TransferListeners. The motivation and design is consistent as outlined in #2729. It also addresses some customer asks as mentioned in #37. For more context, see the discussion in #2770. Every @SdkPublicApi has been thoroughly documented with its description and usage instructions where applicable.
1 parent de77ae5 commit da53f77

36 files changed

+2421
-62
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "S3TransferManager",
3+
"contributor": "",
4+
"type": "feature",
5+
"description": "Add support for S3TransferManager TransferListeners"
6+
}

core/annotations/src/main/java/software/amazon/awssdk/annotations/Immutable.java

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
* Based on code developed by Brian Goetz and Tim Peierls and concepts
4141
* published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls,
4242
* Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea.
43+
*
44+
* @see Mutable
4345
*/
4446
@Documented
4547
@Target(ElementType.TYPE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.annotations;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
/**
25+
* The class to which this annotation is applied is explicitly mutable,
26+
* meaning that its state is subject to change between calls. Mutable
27+
* classes offer no inherent guarantees on thread-safety. Where possible,
28+
* classes may be further annotated as either {@link ThreadSafe} or
29+
* {@link NotThreadSafe}.
30+
*
31+
* @see Immutable
32+
*/
33+
@Documented
34+
@Target(ElementType.TYPE)
35+
@Retention(RetentionPolicy.CLASS)
36+
@SdkProtectedApi
37+
public @interface Mutable {
38+
}

services-custom/s3-transfer-manager/src/it/java/software/amazon/awssdk/transfer/s3/S3TransferManagerDownloadIntegrationTest.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -26,40 +26,45 @@
2626
import org.junit.Test;
2727
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
2828
import software.amazon.awssdk.testutils.RandomTempFile;
29+
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
2930
import software.amazon.awssdk.utils.Md5Utils;
3031

3132
public class S3TransferManagerDownloadIntegrationTest extends S3IntegrationTestBase {
3233
private static final String BUCKET = temporaryBucketName(S3TransferManagerDownloadIntegrationTest.class);
3334
private static final String KEY = "key";
34-
private static S3TransferManager transferManager;
35+
private static final int OBJ_SIZE = 16 * 1024 * 1024;
36+
private static S3TransferManager tm;
3537
private static File file;
3638

3739
@BeforeClass
3840
public static void setup() throws IOException {
3941
createBucket(BUCKET);
40-
file = new RandomTempFile(10_000);
42+
file = new RandomTempFile(OBJ_SIZE);
4143
s3.putObject(PutObjectRequest.builder()
4244
.bucket(BUCKET)
4345
.key(KEY)
4446
.build(), file.toPath());
45-
transferManager = S3TransferManager.builder()
46-
.s3ClientConfiguration(b -> b.region(DEFAULT_REGION)
47+
tm = S3TransferManager.builder()
48+
.s3ClientConfiguration(b -> b.region(DEFAULT_REGION)
4749
.credentialsProvider(CREDENTIALS_PROVIDER_CHAIN))
48-
.build();
50+
.build();
4951
}
5052

5153
@AfterClass
5254
public static void cleanup() {
5355
deleteBucketAndAllContents(BUCKET);
54-
transferManager.close();
56+
tm.close();
5557
S3IntegrationTestBase.cleanUp();
5658
}
5759

5860
@Test
5961
public void download_shouldWork() throws IOException {
6062
Path path = RandomTempFile.randomUncreatedFile().toPath();
61-
Download download = transferManager.download(b -> b.getObjectRequest(r -> r.bucket(BUCKET).key(KEY))
62-
.destination(path));
63+
Download download = tm.download(DownloadRequest.builder()
64+
.getObjectRequest(b -> b.bucket(BUCKET).key(KEY))
65+
.destination(path)
66+
.overrideConfiguration(b -> b.addListener(LoggingTransferListener.create()))
67+
.build());
6368
CompletedDownload completedDownload = download.completionFuture().join();
6469
assertThat(Md5Utils.md5AsBase64(path.toFile())).isEqualTo(Md5Utils.md5AsBase64(file));
6570
assertThat(completedDownload.response().responseMetadata().requestId()).isNotNull();

services-custom/s3-transfer-manager/src/it/java/software/amazon/awssdk/transfer/s3/S3TransferManagerUploadIntegrationTest.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,23 @@
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
1919
import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName;
20+
2021
import java.io.IOException;
2122
import java.nio.file.Files;
2223
import org.junit.AfterClass;
2324
import org.junit.BeforeClass;
2425
import org.junit.Test;
2526
import software.amazon.awssdk.core.ResponseInputStream;
2627
import software.amazon.awssdk.core.sync.ResponseTransformer;
27-
import software.amazon.awssdk.transfer.s3.util.ChecksumUtils;
2828
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
2929
import software.amazon.awssdk.testutils.RandomTempFile;
30+
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
31+
import software.amazon.awssdk.transfer.s3.util.ChecksumUtils;
3032

3133
public class S3TransferManagerUploadIntegrationTest extends S3IntegrationTestBase {
3234
private static final String TEST_BUCKET = temporaryBucketName(S3TransferManagerUploadIntegrationTest.class);
33-
private static final String TEST_KEY = "8mib_file.dat";
34-
private static final int OBJ_SIZE = 8 * 1024 * 1024;
35+
private static final String TEST_KEY = "16mib_file.dat";
36+
private static final int OBJ_SIZE = 16 * 1024 * 1024;
3537

3638
private static RandomTempFile testFile;
3739
private static S3TransferManager tm;
@@ -64,6 +66,7 @@ public void upload_fileSentCorrectly() throws IOException {
6466
Upload upload = tm.upload(UploadRequest.builder()
6567
.putObjectRequest(b -> b.bucket(TEST_BUCKET).key(TEST_KEY))
6668
.source(testFile.toPath())
69+
.overrideConfiguration(b -> b.addListener(LoggingTransferListener.create()))
6770
.build());
6871

6972
CompletedUpload completedUpload = upload.completionFuture().join();

services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/Download.java

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.concurrent.CompletableFuture;
1919
import software.amazon.awssdk.annotations.SdkPreviewApi;
2020
import software.amazon.awssdk.annotations.SdkPublicApi;
21+
import software.amazon.awssdk.transfer.s3.progress.TransferProgress;
2122

2223
/**
2324
* A download transfer of a single object from S3.
@@ -28,4 +29,9 @@ public interface Download extends Transfer {
2829

2930
@Override
3031
CompletableFuture<CompletedDownload> completionFuture();
32+
33+
/**
34+
* The stateful {@link TransferProgress} associated with this transfer.
35+
*/
36+
TransferProgress progress();
3137
}

services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/DownloadRequest.java

+66-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import java.io.File;
1919
import java.nio.file.Path;
2020
import java.util.Objects;
21+
import java.util.Optional;
2122
import java.util.function.Consumer;
2223
import software.amazon.awssdk.annotations.NotThreadSafe;
2324
import software.amazon.awssdk.annotations.SdkPreviewApi;
2425
import software.amazon.awssdk.annotations.SdkPublicApi;
2526
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
27+
import software.amazon.awssdk.utils.ToString;
2628
import software.amazon.awssdk.utils.Validate;
2729
import software.amazon.awssdk.utils.builder.CopyableBuilder;
2830
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -35,10 +37,12 @@
3537
public final class DownloadRequest implements TransferRequest, ToCopyableBuilder<DownloadRequest.Builder, DownloadRequest> {
3638
private final Path destination;
3739
private final GetObjectRequest getObjectRequest;
40+
private final TransferRequestOverrideConfiguration overrideConfiguration;
3841

3942
private DownloadRequest(BuilderImpl builder) {
4043
this.destination = Validate.paramNotNull(builder.destination, "destination");
4144
this.getObjectRequest = Validate.paramNotNull(builder.getObjectRequest, "getObjectRequest");
45+
this.overrideConfiguration = builder.configuration;
4246
}
4347

4448
/**
@@ -72,6 +76,23 @@ public GetObjectRequest getObjectRequest() {
7276
return getObjectRequest;
7377
}
7478

79+
/**
80+
* @return the optional override configuration
81+
* @see Builder#overrideConfiguration(TransferRequestOverrideConfiguration)
82+
*/
83+
public Optional<TransferRequestOverrideConfiguration> overrideConfiguration() {
84+
return Optional.ofNullable(overrideConfiguration);
85+
}
86+
87+
@Override
88+
public String toString() {
89+
return ToString.builder("DownloadRequest")
90+
.add("destination", destination)
91+
.add("getObjectRequest", getObjectRequest)
92+
.add("overrideConfiguration", overrideConfiguration)
93+
.build();
94+
}
95+
7596
@Override
7697
public boolean equals(Object o) {
7798
if (this == o) {
@@ -86,13 +107,17 @@ public boolean equals(Object o) {
86107
if (!Objects.equals(destination, that.destination)) {
87108
return false;
88109
}
89-
return Objects.equals(getObjectRequest, that.getObjectRequest);
110+
if (!Objects.equals(getObjectRequest, that.getObjectRequest)) {
111+
return false;
112+
}
113+
return Objects.equals(overrideConfiguration, that.overrideConfiguration);
90114
}
91115

92116
@Override
93117
public int hashCode() {
94118
int result = destination != null ? destination.hashCode() : 0;
95119
result = 31 * result + (getObjectRequest != null ? getObjectRequest.hashCode() : 0);
120+
result = 31 * result + (overrideConfiguration != null ? overrideConfiguration.hashCode() : 0);
96121
return result;
97122
}
98123

@@ -157,6 +182,31 @@ default Builder getObjectRequest(Consumer<GetObjectRequest.Builder> getObjectReq
157182
return this;
158183
}
159184

185+
/**
186+
* Add an optional request override configuration.
187+
*
188+
* @param configuration The override configuration.
189+
* @return This builder for method chaining.
190+
*/
191+
Builder overrideConfiguration(TransferRequestOverrideConfiguration configuration);
192+
193+
/**
194+
* Similar to {@link #overrideConfiguration(TransferRequestOverrideConfiguration)}, but takes a lambda to configure a new
195+
* {@link TransferRequestOverrideConfiguration.Builder}. This removes the need to call
196+
* {@link TransferRequestOverrideConfiguration#builder()} and
197+
* {@link TransferRequestOverrideConfiguration.Builder#build()}.
198+
*
199+
* @param configurationBuilder the upload configuration
200+
* @return this builder for method chaining.
201+
* @see #overrideConfiguration(TransferRequestOverrideConfiguration)
202+
*/
203+
default Builder overrideConfiguration(Consumer<TransferRequestOverrideConfiguration.Builder> configurationBuilder) {
204+
Validate.paramNotNull(configurationBuilder, "configurationBuilder");
205+
return overrideConfiguration(TransferRequestOverrideConfiguration.builder()
206+
.applyMutation(configurationBuilder)
207+
.build());
208+
}
209+
160210
/**
161211
* @return The built request.
162212
*/
@@ -167,6 +217,7 @@ default Builder getObjectRequest(Consumer<GetObjectRequest.Builder> getObjectReq
167217
private static final class BuilderImpl implements Builder {
168218
private Path destination;
169219
private GetObjectRequest getObjectRequest;
220+
private TransferRequestOverrideConfiguration configuration;
170221

171222
private BuilderImpl() {
172223
}
@@ -199,6 +250,20 @@ public void setGetObjectRequest(GetObjectRequest getObjectRequest) {
199250
getObjectRequest(getObjectRequest);
200251
}
201252

253+
@Override
254+
public Builder overrideConfiguration(TransferRequestOverrideConfiguration configuration) {
255+
this.configuration = configuration;
256+
return this;
257+
}
258+
259+
public void setOverrideConfiguration(TransferRequestOverrideConfiguration configuration) {
260+
overrideConfiguration(configuration);
261+
}
262+
263+
public TransferRequestOverrideConfiguration getOverrideConfiguration() {
264+
return configuration;
265+
}
266+
202267
@Override
203268
public DownloadRequest build() {
204269
return new DownloadRequest(this);

0 commit comments

Comments
 (0)