Skip to content

Commit 04b351e

Browse files
author
Bennett Lynch
authored
[Hackathon] Add support for async doesBucketExist & doesObjectExist (#3075)
* [Hackathon] Add support for async doesBucketExist & doesObjectExist
1 parent 035092b commit 04b351e

File tree

8 files changed

+135
-30
lines changed

8 files changed

+135
-30
lines changed

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/DefaultFailedExecutionContext.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515

1616
package software.amazon.awssdk.core.internal.interceptor;
1717

18+
import static software.amazon.awssdk.utils.CompletableFutureUtils.unwrap;
19+
1820
import java.util.Optional;
19-
import java.util.concurrent.CompletionException;
2021
import software.amazon.awssdk.annotations.SdkInternalApi;
2122
import software.amazon.awssdk.core.SdkRequest;
2223
import software.amazon.awssdk.core.SdkResponse;
@@ -43,13 +44,6 @@ private DefaultFailedExecutionContext(Builder builder) {
4344
this.interceptorContext = Validate.paramNotNull(builder.interceptorContext, "interceptorContext");
4445
}
4546

46-
private Throwable unwrap(Throwable exception) {
47-
while (exception instanceof CompletionException) {
48-
exception = exception.getCause();
49-
}
50-
return exception;
51-
}
52-
5347
@Override
5448
public SdkRequest request() {
5549
return interceptorContext.request();

services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/progress/TransferListenerFailedContext.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
package software.amazon.awssdk.transfer.s3.internal.progress;
1717

18-
import java.util.concurrent.CompletionException;
18+
import static software.amazon.awssdk.utils.CompletableFutureUtils.unwrap;
19+
1920
import software.amazon.awssdk.annotations.Immutable;
2021
import software.amazon.awssdk.annotations.SdkInternalApi;
2122
import software.amazon.awssdk.transfer.s3.TransferObjectRequest;
@@ -45,13 +46,6 @@ private TransferListenerFailedContext(Builder builder) {
4546
this.transferContext = Validate.paramNotNull(builder.transferContext, "transferContext");
4647
}
4748

48-
private Throwable unwrap(Throwable exception) {
49-
while (exception instanceof CompletionException) {
50-
exception = exception.getCause();
51-
}
52-
return exception;
53-
}
54-
5549
public static Builder builder() {
5650
return new Builder();
5751
}

services/s3/src/it/java/software/amazon/awssdk/services/s3/extensions/DefaultS3ExtensionIntegrationTest.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,42 @@ public static void tearDownFixture() {
4848
}
4949

5050
@Test
51-
public void bucketExists() {
51+
public void bucketExistsSync() {
5252
assertThat(s3.doesBucketExist(BUCKET)).isTrue();
5353
}
5454

5555
@Test
56-
public void bucketDoesNotExist() {
56+
public void bucketExistsAsync() {
57+
assertThat(s3Async.doesBucketExist(BUCKET).join()).isTrue();
58+
}
59+
60+
@Test
61+
public void bucketDoesNotExistSync() {
5762
assertThat(s3.doesBucketExist(temporaryBucketName("noexist"))).isFalse();
5863
}
5964

6065
@Test
61-
public void objectExists() {
66+
public void bucketDoesNotExistAsync() {
67+
assertThat(s3Async.doesBucketExist(temporaryBucketName("noexist")).join()).isFalse();
68+
}
69+
70+
@Test
71+
public void objectExistsSync() {
6272
assertThat(s3.doesObjectExist(BUCKET, KEY)).isTrue();
6373
}
6474

6575
@Test
66-
public void objectDoesNotExist() {
76+
public void objectExistsAsync() {
77+
assertThat(s3Async.doesObjectExist(BUCKET, KEY).join()).isTrue();
78+
}
79+
80+
@Test
81+
public void objectDoesNotExistSync() {
6782
assertThat(s3.doesObjectExist(BUCKET, "noexist")).isFalse();
6883
}
84+
85+
@Test
86+
public void objectDoesNotExistAsync() {
87+
assertThat(s3Async.doesObjectExist(BUCKET, "noexist").join()).isFalse();
88+
}
6989
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/extensions/S3AsyncClientSdkExtension.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import software.amazon.awssdk.annotations.SdkExtensionMethod;
2020
import software.amazon.awssdk.annotations.SdkPublicApi;
2121
import software.amazon.awssdk.services.s3.S3AsyncClient;
22+
import software.amazon.awssdk.services.s3.internal.extensions.DefaultS3AsyncClientSdkExtension;
2223
import software.amazon.awssdk.services.s3.model.S3Exception;
2324

2425
/**
@@ -40,21 +41,21 @@ public interface S3AsyncClientSdkExtension {
4041
*/
4142
@SdkExtensionMethod
4243
default CompletableFuture<Boolean> doesBucketExist(String bucket) {
43-
throw new UnsupportedOperationException("not implemented yet");
44+
return new DefaultS3AsyncClientSdkExtension((S3AsyncClient) this).doesBucketExist(bucket);
4445
}
4546

4647
/**
4748
* Check whether the specified object exists in Amazon S3 (and you have permission to access it). If the object exists but is
4849
* not accessible (e.g., due to access being denied or the bucket existing in another region), an {@link S3Exception} will be
4950
* thrown.
5051
*
51-
* @param bucketName the bucket that contains the object
52-
* @param key the name of the object
52+
* @param bucket the bucket that contains the object
53+
* @param key the name of the object
5354
* @return true if the bucket object exists and you have permission to access it; false if it does not exist
5455
* @throws S3Exception if the bucket exists but is not accessible
5556
*/
5657
@SdkExtensionMethod
57-
default CompletableFuture<Boolean> doesObjectExist(String bucketName, String key) {
58-
throw new UnsupportedOperationException("not implemented yet");
58+
default CompletableFuture<Boolean> doesObjectExist(String bucket, String key) {
59+
return new DefaultS3AsyncClientSdkExtension((S3AsyncClient) this).doesObjectExist(bucket, key);
5960
}
6061
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/extensions/S3ClientSdkExtension.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ default boolean doesBucketExist(String bucket) {
5959
* not accessible (e.g., due to access being denied or the bucket existing in another region), an {@link S3Exception} will be
6060
* thrown.
6161
*
62-
* @param bucketName the bucket that contains the object
62+
* @param bucket the bucket that contains the object
6363
* @param key the name of the object
6464
* @return true if the bucket object exists and you have permission to access it; false if it does not exist
6565
* @throws S3Exception if the bucket exists but is not accessible
6666
*/
6767
@SdkExtensionMethod
68-
default boolean doesObjectExist(String bucketName, String key) {
69-
return new DefaultS3ClientSdkExtension((S3Client) this).doesObjectExist(bucketName, key);
68+
default boolean doesObjectExist(String bucket, String key) {
69+
return new DefaultS3ClientSdkExtension((S3Client) this).doesObjectExist(bucket, key);
7070
}
7171

7272
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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.services.s3.internal.extensions;
17+
18+
import static software.amazon.awssdk.utils.CompletableFutureUtils.failedFuture;
19+
import static software.amazon.awssdk.utils.CompletableFutureUtils.forwardExceptionTo;
20+
import static software.amazon.awssdk.utils.CompletableFutureUtils.unwrap;
21+
22+
import java.util.concurrent.CompletableFuture;
23+
import software.amazon.awssdk.services.s3.S3AsyncClient;
24+
import software.amazon.awssdk.services.s3.extensions.S3AsyncClientSdkExtension;
25+
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
26+
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
27+
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
28+
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
29+
import software.amazon.awssdk.utils.Validate;
30+
31+
public class DefaultS3AsyncClientSdkExtension implements S3AsyncClientSdkExtension {
32+
33+
private final S3AsyncClient s3;
34+
35+
public DefaultS3AsyncClientSdkExtension(S3AsyncClient s3) {
36+
this.s3 = Validate.notNull(s3, "s3");
37+
}
38+
39+
@Override
40+
public CompletableFuture<Boolean> doesBucketExist(String bucket) {
41+
try {
42+
Validate.notEmpty(bucket, "bucket");
43+
CompletableFuture<Boolean> returnFuture = new CompletableFuture<>();
44+
CompletableFuture<HeadBucketResponse> responseFuture = s3.headBucket(r -> r.bucket(bucket));
45+
forwardExceptionTo(returnFuture, responseFuture);
46+
responseFuture.whenComplete((r, t) -> {
47+
t = unwrap(t);
48+
if (t == null) {
49+
returnFuture.complete(true);
50+
} else if (t instanceof NoSuchBucketException) {
51+
returnFuture.complete(false);
52+
} else {
53+
returnFuture.completeExceptionally(t);
54+
}
55+
});
56+
return returnFuture;
57+
} catch (Throwable t) {
58+
return failedFuture(t);
59+
}
60+
}
61+
62+
@Override
63+
public CompletableFuture<Boolean> doesObjectExist(String bucket, String key) {
64+
try {
65+
Validate.notEmpty(bucket, "bucket");
66+
Validate.notEmpty(bucket, "key");
67+
CompletableFuture<Boolean> returnFuture = new CompletableFuture<>();
68+
CompletableFuture<HeadObjectResponse> responseFuture = s3.headObject(r -> r.bucket(bucket).key(key));
69+
forwardExceptionTo(returnFuture, responseFuture);
70+
responseFuture.whenComplete((r, t) -> {
71+
t = unwrap(t);
72+
if (t == null) {
73+
returnFuture.complete(true);
74+
} else if (t instanceof NoSuchKeyException) {
75+
returnFuture.complete(false);
76+
} else {
77+
returnFuture.completeExceptionally(t);
78+
}
79+
});
80+
return returnFuture;
81+
} catch (Throwable t) {
82+
return failedFuture(t);
83+
}
84+
}
85+
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/extensions/DefaultS3ClientSdkExtension.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ public boolean doesBucketExist(String bucket) {
4444

4545
@Override
4646
public boolean doesObjectExist(String bucket, String key) {
47-
Validate.notNull(bucket, "bucket");
48-
Validate.notNull(bucket, "key");
47+
Validate.notEmpty(bucket, "bucket");
48+
Validate.notEmpty(bucket, "key");
4949
try {
5050
s3.headObject(r -> r.bucket(bucket).key(key));
5151
return true;

utils/src/main/java/software/amazon/awssdk/utils/CompletableFutureUtils.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,15 @@ public static <SourceT, DestT> CompletableFuture<SourceT> forwardTransformedResu
144144

145145
return src;
146146
}
147+
148+
/**
149+
* Unwrap a {@link Throwable} provided by a {@link CompletableFuture} to discard the {@link CompletionException} and retrieve
150+
* the underlying exception.
151+
*/
152+
public static Throwable unwrap(Throwable exception) {
153+
while (exception instanceof CompletionException) {
154+
exception = exception.getCause();
155+
}
156+
return exception;
157+
}
147158
}

0 commit comments

Comments
 (0)