Skip to content

Commit 893b493

Browse files
authored
Merge branch 'feature/master/transfermanager' into feature/master/transfermanager
2 parents 03df45f + 3af86a0 commit 893b493

File tree

6 files changed

+383
-3
lines changed

6 files changed

+383
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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.transfer.s3.internal;
17+
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
import java.util.Optional;
21+
import software.amazon.awssdk.annotations.SdkInternalApi;
22+
import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
23+
import software.amazon.awssdk.core.exception.SdkClientException;
24+
import software.amazon.awssdk.core.exception.SdkServiceException;
25+
import software.amazon.awssdk.crt.s3.CrtS3RuntimeException;
26+
import software.amazon.awssdk.services.s3.model.BucketAlreadyExistsException;
27+
import software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException;
28+
import software.amazon.awssdk.services.s3.model.InvalidObjectStateException;
29+
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
30+
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
31+
import software.amazon.awssdk.services.s3.model.NoSuchUploadException;
32+
import software.amazon.awssdk.services.s3.model.ObjectAlreadyInActiveTierErrorException;
33+
import software.amazon.awssdk.services.s3.model.S3Exception;
34+
import software.amazon.awssdk.utils.StringUtils;
35+
36+
@SdkInternalApi
37+
public class CrtErrorHandler {
38+
39+
private final Map<String, S3Exception.Builder> s3ExceptionBuilderMap;
40+
41+
public CrtErrorHandler() {
42+
s3ExceptionBuilderMap = getS3ExceptionBuilderMap();
43+
}
44+
45+
/**
46+
* This class transform a crtTRunTimeException to the S3 service Exceptions.
47+
* CrtS3RuntimeException are the exceptions generated due to failures in CRTClient due to S3 Service errors.
48+
*
49+
* @param crtRuntimeException Exception that is thrown by CrtClient.
50+
* @return
51+
*/
52+
public Exception transformException(Exception crtRuntimeException) {
53+
Optional<CrtS3RuntimeException> crtS3RuntimeExceptionOptional = getCrtS3RuntimeException(crtRuntimeException);
54+
return crtS3RuntimeExceptionOptional
55+
.filter(CrtErrorHandler::isErrorDetailsAvailable)
56+
.map(e -> getServiceSideException(e))
57+
.orElse(SdkClientException.create(crtRuntimeException.getMessage(), crtRuntimeException));
58+
}
59+
60+
private Exception getServiceSideException(CrtS3RuntimeException e) {
61+
if (s3ExceptionBuilderMap.get(e.getAwsErrorCode()) != null) {
62+
return s3ExceptionBuilderMap.get(e.getAwsErrorCode())
63+
.awsErrorDetails(
64+
AwsErrorDetails.builder().errorCode(e.getAwsErrorCode())
65+
.errorMessage(e.getAwsErrorMessage()).build())
66+
.cause(e)
67+
.message(e.getMessage())
68+
.statusCode(e.getStatusCode())
69+
.build();
70+
}
71+
return SdkServiceException.builder().statusCode(e.getStatusCode()).message(e.getMessage()).cause(e).build();
72+
}
73+
74+
/**
75+
* This method checks if the exception has the required details to transform to S3 Exception.
76+
* @param crtS3RuntimeException the exception that needs to be checked
77+
* @return true if exception has the required details.
78+
*/
79+
private static boolean isErrorDetailsAvailable(CrtS3RuntimeException crtS3RuntimeException) {
80+
return StringUtils.isNotBlank(crtS3RuntimeException.getAwsErrorCode());
81+
}
82+
83+
/**
84+
* Checks if the Exception or its cause is of CrtS3RuntimeException.
85+
* The S3 Service related exception are in the form of CrtS3RuntimeException.
86+
* @param crtRuntimeException
87+
* @return CrtS3RuntimeException else return empty,
88+
*/
89+
private Optional<CrtS3RuntimeException> getCrtS3RuntimeException(Exception crtRuntimeException) {
90+
if (crtRuntimeException instanceof CrtS3RuntimeException) {
91+
return Optional.of((CrtS3RuntimeException) crtRuntimeException);
92+
}
93+
Throwable cause = crtRuntimeException.getCause();
94+
if (cause instanceof CrtS3RuntimeException) {
95+
return Optional.of((CrtS3RuntimeException) cause);
96+
}
97+
return Optional.empty();
98+
}
99+
100+
101+
/**
102+
* Gets a Mapping of AWSErrorCode to its corresponding S3 Exception Builders.
103+
*
104+
* @return
105+
*/
106+
private Map<String, S3Exception.Builder> getS3ExceptionBuilderMap() {
107+
Map<String, S3Exception.Builder> s3ExceptionBuilderMap = new HashMap<>();
108+
s3ExceptionBuilderMap.put("ObjectAlreadyInActiveTierError", ObjectAlreadyInActiveTierErrorException.builder());
109+
s3ExceptionBuilderMap.put("NoSuchUpload", NoSuchUploadException.builder());
110+
s3ExceptionBuilderMap.put("BucketAlreadyExists", BucketAlreadyExistsException.builder());
111+
s3ExceptionBuilderMap.put("BucketAlreadyOwnedByYou", BucketAlreadyOwnedByYouException.builder());
112+
s3ExceptionBuilderMap.put("InvalidObjectState", InvalidObjectStateException.builder());
113+
s3ExceptionBuilderMap.put("NoSuchBucket", NoSuchBucketException.builder());
114+
s3ExceptionBuilderMap.put("NoSuchKey", NoSuchKeyException.builder());
115+
return s3ExceptionBuilderMap;
116+
}
117+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class CrtResponseDataConsumerAdapter<ReturnT> implements ResponseDataCons
3939
private final CompletableFuture<ReturnT> future;
4040
private final S3CrtDataPublisher publisher;
4141
private final ResponseHeadersHandler headerHandler;
42+
private final CrtErrorHandler errorHandler;
4243

4344
public CrtResponseDataConsumerAdapter(AsyncResponseTransformer<GetObjectResponse, ReturnT> transformer) {
4445
this(transformer, new S3CrtDataPublisher(), new ResponseHeadersHandler());
@@ -52,6 +53,7 @@ public CrtResponseDataConsumerAdapter(AsyncResponseTransformer<GetObjectResponse
5253
this.future = transformer.prepare();
5354
this.publisher = s3CrtDataPublisher;
5455
this.headerHandler = headersHandler;
56+
this.errorHandler = new CrtErrorHandler();
5557
}
5658

5759
public CompletableFuture<ReturnT> transformerFuture() {
@@ -87,8 +89,9 @@ public void onResponseData(ByteBuffer byteBuffer) {
8789
@Override
8890
public void onException(CrtRuntimeException e) {
8991
log.debug(() -> "An error occurred ", e);
90-
transformer.exceptionOccurred(e);
91-
publisher.notifyError(e);
92+
Exception transformException = errorHandler.transformException(e);
93+
transformer.exceptionOccurred(transformException);
94+
publisher.notifyError(transformException);
9295
}
9396

9497
@Override

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
public final class DefaultS3CrtAsyncClient implements S3CrtAsyncClient {
3838
private final S3NativeClient s3NativeClient;
3939
private final S3NativeClientConfiguration configuration;
40+
private final CrtErrorHandler crtErrorHandler;
4041

4142
public DefaultS3CrtAsyncClient(DefaultS3CrtClientBuilder builder) {
4243
S3NativeClientConfiguration.Builder configBuilder =
@@ -58,13 +59,15 @@ public DefaultS3CrtAsyncClient(DefaultS3CrtClientBuilder builder) {
5859
configuration.partSizeBytes(),
5960
configuration.targetThroughputInGbps(),
6061
configuration.maxConcurrency());
62+
crtErrorHandler = new CrtErrorHandler();
6163
}
6264

6365
@SdkTestInternalApi
6466
DefaultS3CrtAsyncClient(S3NativeClientConfiguration configuration,
6567
S3NativeClient nativeClient) {
6668
this.configuration = configuration;
6769
this.s3NativeClient = nativeClient;
70+
crtErrorHandler = new CrtErrorHandler();
6871
}
6972

7073
@Override
@@ -96,6 +99,7 @@ public CompletableFuture<PutObjectResponse> putObject(PutObjectRequest putObject
9699
CompletableFuture<PutObjectResponse> returnFuture = new CompletableFuture<>();
97100

98101
com.amazonaws.s3.model.PutObjectRequest adaptedRequest = S3CrtPojoConversion.toCrtPutObjectRequest(putObjectRequest);
102+
CompletableFuture<PutObjectResponse> returnFuture = new CompletableFuture<>();
99103

100104
if (adaptedRequest.contentLength() == null && requestBody.contentLength().isPresent()) {
101105
adaptedRequest = adaptedRequest.toBuilder()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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.transfer.s3.internal;
17+
18+
import static org.mockito.Mockito.when;
19+
20+
import org.assertj.core.api.Assertions;
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.mockito.Mock;
24+
import org.mockito.runners.MockitoJUnitRunner;
25+
import software.amazon.awssdk.core.exception.SdkClientException;
26+
import software.amazon.awssdk.core.exception.SdkServiceException;
27+
import software.amazon.awssdk.crt.CrtRuntimeException;
28+
import software.amazon.awssdk.crt.s3.CrtS3RuntimeException;
29+
import software.amazon.awssdk.services.s3.model.BucketAlreadyExistsException;
30+
import software.amazon.awssdk.services.s3.model.InvalidObjectStateException;
31+
32+
@RunWith(MockitoJUnitRunner.class)
33+
public class CrtErrorHandlerTest {
34+
35+
@Mock
36+
private CrtS3RuntimeException mockCrtS3RuntimeException;
37+
38+
@Test
39+
public void crtS3ExceptionAreTransformed(){
40+
CrtErrorHandler crtErrorHandler = new CrtErrorHandler();
41+
when(mockCrtS3RuntimeException.getAwsErrorCode()).thenReturn("BucketAlreadyExists");
42+
when(mockCrtS3RuntimeException.getAwsErrorMessage()).thenReturn("Bucket Already Exists");
43+
when(mockCrtS3RuntimeException.getStatusCode()).thenReturn(404);
44+
Exception transformException = crtErrorHandler.transformException(mockCrtS3RuntimeException);
45+
Assertions.assertThat(transformException).isInstanceOf(BucketAlreadyExistsException.class);
46+
Assertions.assertThat(transformException.getMessage()).contains("Bucket Already Exists");
47+
}
48+
49+
@Test
50+
public void nonCrtS3ExceptionAreNotTransformed(){
51+
CrtErrorHandler crtErrorHandler = new CrtErrorHandler();
52+
Exception transformException = crtErrorHandler.transformException(new CrtRuntimeException("AWS_ERROR"));
53+
Assertions.assertThat(transformException).isInstanceOf(SdkClientException.class);
54+
}
55+
56+
57+
@Test
58+
public void crtS3ExceptionAreTransformedWhenExceptionIsInCause(){
59+
CrtErrorHandler crtErrorHandler = new CrtErrorHandler();
60+
when(mockCrtS3RuntimeException.getAwsErrorCode()).thenReturn("InvalidObjectState");
61+
when(mockCrtS3RuntimeException.getAwsErrorMessage()).thenReturn("Invalid Object State");
62+
when(mockCrtS3RuntimeException.getStatusCode()).thenReturn(404);
63+
final Exception transformException = crtErrorHandler.transformException(new Exception("Some Exception", mockCrtS3RuntimeException));
64+
65+
System.out.println("transformException " +transformException);
66+
67+
Assertions.assertThat(transformException).isInstanceOf(InvalidObjectStateException.class);
68+
Assertions.assertThat(transformException.getMessage()).contains("Invalid Object State");
69+
Assertions.assertThat(transformException.getCause()).isInstanceOf(CrtS3RuntimeException.class);
70+
}
71+
72+
@Test
73+
public void nonCrtS3ExceptionAreNotTransformedWhenExceptionIsInCause(){
74+
CrtErrorHandler crtErrorHandler = new CrtErrorHandler();
75+
final Exception crtRuntimeException = new Exception("Some Exception", new CrtRuntimeException("AWS_ERROR"));
76+
Exception transformException = crtErrorHandler.transformException(
77+
crtRuntimeException);
78+
Assertions.assertThat(transformException).isNotInstanceOf(CrtRuntimeException.class);
79+
Assertions.assertThat(transformException).isInstanceOf(SdkClientException.class);
80+
Assertions.assertThat(transformException.getMessage()).isEqualTo("Some Exception");
81+
Assertions.assertThat(transformException.getCause()).isEqualTo(crtRuntimeException);
82+
}
83+
84+
@Test
85+
public void crtS3ExceptionWithErrorCodeNodeNotInS3Model() {
86+
CrtErrorHandler crtErrorHandler = new CrtErrorHandler();
87+
when(mockCrtS3RuntimeException.getAwsErrorCode()).thenReturn("NewS3ExceptionFromCrt");
88+
when(mockCrtS3RuntimeException.getAwsErrorMessage()).thenReturn("New S3 Exception From Crt");
89+
when(mockCrtS3RuntimeException.getStatusCode()).thenReturn(404);
90+
Exception transformException = crtErrorHandler.transformException(mockCrtS3RuntimeException);
91+
Assertions.assertThat(transformException).isInstanceOf(SdkServiceException.class);
92+
Assertions.assertThat(transformException.getCause()).isEqualTo(mockCrtS3RuntimeException);
93+
Assertions.assertThat(transformException.getMessage()).isEqualTo(mockCrtS3RuntimeException.getMessage());
94+
Assertions.assertThat(((SdkServiceException)transformException).statusCode())
95+
.isEqualTo(mockCrtS3RuntimeException.getStatusCode());
96+
}
97+
98+
@Test
99+
public void crtS3ExceptionInCauseWithErrorCodeNodeNotInS3Model() {
100+
CrtErrorHandler crtErrorHandler = new CrtErrorHandler();
101+
when(mockCrtS3RuntimeException.getAwsErrorCode()).thenReturn("NewS3ExceptionFromCrt");
102+
when(mockCrtS3RuntimeException.getAwsErrorMessage()).thenReturn("New S3 Exception From Crt");
103+
when(mockCrtS3RuntimeException.getStatusCode()).thenReturn(404);
104+
final Exception crtRuntimeException = new Exception(mockCrtS3RuntimeException);
105+
Exception transformException = crtErrorHandler.transformException(crtRuntimeException);
106+
Assertions.assertThat(transformException).isInstanceOf(SdkServiceException.class);
107+
Assertions.assertThat(transformException.getCause()).isEqualTo(mockCrtS3RuntimeException);
108+
Assertions.assertThat(transformException.getMessage()).isEqualTo(mockCrtS3RuntimeException.getMessage());
109+
Assertions.assertThat(((SdkServiceException) transformException).statusCode()).isEqualTo(404);
110+
}
111+
}

0 commit comments

Comments
 (0)