CLOVER🍀

That was when it all began.

AWS SDK for Java 2.xでAPI呌び出しの結果が゚ラヌの堎合、䟋倖がスロヌされるのかSdkHttpResponseのisSuccessfulは

これは、なにをしたくお曞いたもの

AWS SDK for Java 2.xを䜿うず、APIの各皮レスポンスからSdkHttpResponseずいうむンタヌフェヌスのむンスタンスを取埗するこずが
できたす。

こちらですね。

SdkHttpResponse (AWS SDK for Java - 2.20.17)

このクラスにはisSuccessfulずいう成功倱敗を刀定できそうなメ゜ッドがありたす。

If we get back any 2xx status code, then we know we should treat the service call as successful.

APIの呌び出し結果を扱う時に、゚ラヌハンドリングずしおこの倀を芋た方がいいのかどうなのかよくわからなかったので、ちょっず
調べおみるこずにしたした。

Amazon S3を䟋に

Amazon S3を䟋に、ちょっず芋おみたしょう。

Amazon S3にアクセスする際には、S3ClientずいうむンタヌフェヌスたたはS3AsyncClientを䜿いたす。

S3Client (AWS SDK for Java - 2.20.17)

バケットを䜜成するcreateBucketを芋おみるず、問題があった時には以䞋のように䟋倖がスロヌされるであろうこずがわかりたす。

default CreateBucketResponse createBucket(CreateBucketRequest createBucketRequest)
                                   throws BucketAlreadyExistsException,
                                          BucketAlreadyOwnedByYouException,
                                          AwsServiceException,
                                          SdkClientException,
                                          S3Exception

䞀方で、その戻り倀であるCreateBucketResponseの定矩を芋るず、sdkHttpResponseずいうメ゜ッドがあり。

CreateBucketResponse (AWS SDK for Java - 2.20.17)

こちらから先述のSdkHttpResponseを埗るこずができ、isSuccessfulずいうメ゜ッドを備えおいたす。

これを芋おいるず、スロヌされる䟋倖ずは別にSdkHttpResponse#isSuccessfulの結果も芋ないずいけないのではず思っおしたったのが
今回の゚ントリヌを曞いた理由ですね。

ドキュメントはどう蚀っおいるか

ここで、AWS SDK for Java 2.xの䟋倖凊理のドキュメントを芋おみたしょう。

AWS SDK for Java 2.x の例外処理 - AWS SDK for Java 2.x

AwsServiceExceptionおよびそのサブクラスに関する蚘述を芋るず、API呌び出し時に゚ラヌが発生した時はAwsServiceExceptionの
むンスタンスおよびそのサブクラス含むがスロヌされるこずが曞かれおいたす。

AWS SDK for Java 2.x の䟋倖凊理 / AwsServiceException (およびサブクラス)

たずえば、最初に曞いたS3Client#createBucketメ゜ッドからスロヌされるずされおいるBucketAlreadyOwnedByYouExceptionクラスも、
AwsServiceExceptionのサブクラスですね。

BucketAlreadyOwnedByYouException (AWS SDK for Java - 2.20.17)

䟋倖の詳现に぀いおは、AwsErrorDetailsクラスを確認すればよい、ずなっおいたす。

AwsErrorDetails (AWS SDK for Java - 2.20.17)

こちらのerrorCodeから、各サヌビスのドキュメントを確認するこずができたす。たずえば、Amazon S3の堎合はこちら。

Error Responses / List of Error Codes

AwsErrorDetailsのむンスタンスは、AwsServiceExceptionから取埗できたす。

AwsServiceException (AWS SDK for Java - 2.20.17)

たた、そもそもAPI呌び出しができないような状況の時にはSdkClientExceptionがスロヌされる、ずも曞かれおいたす。

AWS SDK for Java 2.x の䟋倖凊理 / SdkClientException

こちらですね。

SdkClientException (AWS SDK for Java - 2.20.17)

どちらにせよ、SdkHttpResponseむンタヌフェヌスはたったく登堎したせん。

このこずから、APIの呌び出しに倱敗するず䟋倖がスロヌされるので、API呌び出しの成功確認にSdkHttpResponse#isSuccessfulメ゜ッドを
参照しなくおも良さそうな気がしたすね。

今回、こちらをもう少し远っおみたしょう。LocalStackずAWS SDK for Java 2.xのS3Clientを䜿っお詊しおみたいず思いたす。

環境

今回の環境は、こちら。

LocalStack。

$ python3 -V
Python 3.10.6


$ localstack --version
1.4.0

起動。

$ localstack start

Javaたわり。

$ java --version
openjdk 17.0.6 2023-01-17
OpenJDK Runtime Environment (build 17.0.6+10-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 17.0.6+10-Ubuntu-0ubuntu122.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.0 (9b58d2bad23a66be161c4664ef21ce219c2c8584)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.6, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-67-generic", arch: "amd64", family: "unix"

準備

Maven䟝存関係などは、こちら。

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>2.20.17</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
        </dependency>
    </dependencies>

サンプルプログラムを曞く

今回甚意したプログラムは、こちら。

src/main/java/org/littlewings/aws/App.java

package org.littlewings.aws;

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.awscore.defaultsmode.DefaultsMode;
import software.amazon.awssdk.awscore.exception.AwsErrorDetails;
import software.amazon.awssdk.awscore.exception.AwsServiceException;
import software.amazon.awssdk.http.HttpStatusFamily;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.CreateBucketResponse;

import java.net.URI;

public class App {
    public static void main(String... args) {
        try (S3Client s3Client = S3Client
                                    .builder()
                                    .credentialsProvider(
                                            StaticCredentialsProvider.create(AwsBasicCredentials.create("access_key_id", "secret_access_key"))
                                    )
                                    .region(Region.AP_EAST_1)
                                    .defaultsMode(DefaultsMode.AUTO)
                                    .endpointOverride(URI.create("http://localhost:4566"))
                                    .forcePathStyle(true)
                                    .build()) {

            CreateBucketRequest request =
                    CreateBucketRequest
                            .builder()
                            .bucket("my-bucket")
                            .build();

            CreateBucketResponse response = s3Client.createBucket(request);

            SdkHttpResponse httpResponse = response.sdkHttpResponse();
            System.out.printf("isSuccessful = %b%n", httpResponse.isSuccessful());
            System.out.printf("statusCode = %d%n", httpResponse.statusCode());
            System.out.printf("httpStatusFamily = %s%n", HttpStatusFamily.of(httpResponse.statusCode()));
        } catch (AwsServiceException e) {
            AwsErrorDetails errorDetails = e.awsErrorDetails();
            System.out.printf("serviceName = %s%n", errorDetails.serviceName());
            System.out.printf("errorMessage = %s%n", errorDetails.errorMessage());
            System.out.printf("errorCode = %s%n", errorDetails.errorCode());

            SdkHttpResponse httpResponse = errorDetails.sdkHttpResponse();
            System.out.printf("isSuccessful = %b%n", httpResponse.isSuccessful());
            System.out.printf("statusCode = %d%n", httpResponse.statusCode());
            System.out.printf("httpStatusFamily = %s%n", HttpStatusFamily.of(httpResponse.statusCode()));

            e.printStackTrace();
        }
    }
}

Amazon S3バケットを䜜成するだけのプログラムです。

            CreateBucketRequest request =
                    CreateBucketRequest
                            .builder()
                            .bucket("my-bucket")
                            .build();

            CreateBucketResponse response = s3Client.createBucket(request);

あずは、レスポンスに含たれるSdkHttpResponseの内容を芋たり、

            SdkHttpResponse httpResponse = response.sdkHttpResponse();
            System.out.printf("isSuccessful = %b%n", httpResponse.isSuccessful());
            System.out.printf("statusCode = %d%n", httpResponse.statusCode());
            System.out.printf("httpStatusFamily = %s%n", HttpStatusFamily.of(httpResponse.statusCode()));

䟋倖ハンドリングを行ったり。

        } catch (AwsServiceException e) {
            AwsErrorDetails errorDetails = e.awsErrorDetails();
            System.out.printf("serviceName = %s%n", errorDetails.serviceName());
            System.out.printf("errorMessage = %s%n", errorDetails.errorMessage());
            System.out.printf("errorCode = %s%n", errorDetails.errorCode());

            SdkHttpResponse httpResponse = errorDetails.sdkHttpResponse();
            System.out.printf("isSuccessful = %b%n", httpResponse.isSuccessful());
            System.out.printf("statusCode = %d%n", httpResponse.statusCode());
            System.out.printf("httpStatusFamily = %s%n", HttpStatusFamily.of(httpResponse.statusCode()));

            e.printStackTrace();
        }

スロヌされる䟋倖はAwsServiceExceptionクラスのサブクラスなので、こちらで䞀気に捕捉するこずにしたす。

確認しおみる

䜜成したプログラムを動かしおみたしょう。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.aws.App

結果。

isSuccessful = true
statusCode = 200
httpStatusFamily = SUCCESSFUL

もう1床実行するず、すでにS3バケットは䜜成されおいるので䟋倖がスロヌされたす。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.aws.App

こんな感じですね。

serviceName = S3
errorMessage = Your previous request to create the named bucket succeeded and you already own it.
errorCode = BucketAlreadyOwnedByYou
isSuccessful = false
statusCode = 409
httpStatusFamily = CLIENT_ERROR
software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException: Your previous request to create the named bucket succeeded and you already own it. (Service: S3, Status Code: 409, Request ID: 44425877V1D0A2F9, Extended Request ID: MzRISOwyjmnup2476E87BD8C5CA707/JypPGXLh0OVFGcJaaO3KW/hRAqKOpIEEp)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handleErrorResponse(AwsXmlPredicatedResponseHandler.java:156)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handleResponse(AwsXmlPredicatedResponseHandler.java:108)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handle(AwsXmlPredicatedResponseHandler.java:85)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handle(AwsXmlPredicatedResponseHandler.java:43)
        at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler$Crc32ValidationResponseHandler.handle(AwsSyncClientHandler.java:95)
        at software.amazon.awssdk.core.internal.handler.BaseClientHandler.lambda$successTransformationResponseHandler$7(BaseClientHandler.java:270)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.HandleResponseStage.execute(HandleResponseStage.java:40)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.HandleResponseStage.execute(HandleResponseStage.java:30)
        at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptTimeoutTrackingStage.execute(ApiCallAttemptTimeoutTrackingStage.java:73)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptTimeoutTrackingStage.execute(ApiCallAttemptTimeoutTrackingStage.java:42)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage.execute(TimeoutExceptionHandlingStage.java:78)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.TimeoutExceptionHandlingStage.execute(TimeoutExceptionHandlingStage.java:40)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptMetricCollectionStage.execute(ApiCallAttemptMetricCollectionStage.java:50)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallAttemptMetricCollectionStage.execute(ApiCallAttemptMetricCollectionStage.java:36)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:81)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:36)
        at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
        at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:56)
        at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:36)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.executeWithTimer(ApiCallTimeoutTrackingStage.java:80)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:60)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:42)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:48)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:31)
        at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
        at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:37)
        at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:26)
        at software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient$RequestExecutionBuilderImpl.execute(AmazonSyncHttpClient.java:193)
        at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.invoke(BaseSyncClientHandler.java:103)
        at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.doExecute(BaseSyncClientHandler.java:171)
        at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.lambda$execute$1(BaseSyncClientHandler.java:82)
        at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.measureApiCallSuccess(BaseSyncClientHandler.java:179)
        at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:76)
        at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:45)
        at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:56)
        at software.amazon.awssdk.services.s3.DefaultS3Client.createBucket(DefaultS3Client.java:1149)
        at org.littlewings.aws.App.main(App.java:36)
        at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:279)
        at java.base/java.lang.Thread.run(Thread.java:833)

今回はBucketAlreadyOwnedByYouExceptionがスロヌされたした。

software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException: Your previous request to create the named bucket succeeded and you already own it. (Service: S3, Status Code: 409, Request ID: 44425877V1D0A2F9, Extended Request ID: MzRISOwyjmnup2476E87BD8C5CA707/JypPGXLh0OVFGcJaaO3KW/hRAqKOpIEEp)

䟋倖に含たれおいるAwsErrorDetailsから埗られた情報はこちら。

serviceName = S3
errorMessage = Your previous request to create the named bucket succeeded and you already own it.
errorCode = BucketAlreadyOwnedByYou

AwsErrorDetailsから取埗できる、SdkHttpResponseの情報はこちら。

isSuccessful = false
statusCode = 409
httpStatusFamily = CLIENT_ERROR

このケヌスではSdkHttpResponse#isSuccessfulがfalseになっおいたす。そもそも、HTTPステヌタスコヌドが2xxの堎合にtrueになる
プロパティですからね。

If we get back any 2xx status code, then we know we should treat the service call as successful.

ずいうわけで、SdkHttpResponse#isSuccessfulがfalseになるケヌスではやはり䟋倖がスロヌされる気がしたす。

実装は

少し、実装を芋おみたしょう。S3Clientむンタヌフェヌスの実装クラスを少し芋おみたす。

$ unzip -p $HOME/.m2/repository/software/amazon/awssdk/s3/2.20.17/s3-2.20.17-sources.jar software/amazon/awssdk/services/s3/DefaultS3Client.java

こんな感じで、゚ラヌコヌドず䟋倖がマッピングされおいる箇所がありたした。

    private AwsS3ProtocolFactory init() {
        return AwsS3ProtocolFactory
                .builder()
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("NoSuchUpload")
                                .exceptionBuilderSupplier(NoSuchUploadException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("ObjectAlreadyInActiveTierError")
                                .exceptionBuilderSupplier(ObjectAlreadyInActiveTierErrorException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("BucketAlreadyExists")
                                .exceptionBuilderSupplier(BucketAlreadyExistsException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("NoSuchBucket")
                                .exceptionBuilderSupplier(NoSuchBucketException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("InvalidObjectState")
                                .exceptionBuilderSupplier(InvalidObjectStateException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("ObjectNotInActiveTierError")
                                .exceptionBuilderSupplier(ObjectNotInActiveTierErrorException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("BucketAlreadyOwnedByYou")
                                .exceptionBuilderSupplier(BucketAlreadyOwnedByYouException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("NoSuchKey").exceptionBuilderSupplier(NoSuchKeyException::builder)
                                .build()).clientConfiguration(clientConfiguration)
                .defaultServiceExceptionSupplier(S3Exception::builder).build();
    }

先ほどのサンプルアプリケヌションでS3バケットの䜜成倱敗時に、AwsErrorDetailsから埗られた゚ラヌコヌドはBucketAlreadyOwnedByYou
なので、こちらが該圓したすね。

                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("BucketAlreadyOwnedByYou")
                                .exceptionBuilderSupplier(BucketAlreadyOwnedByYouException::builder).build())

マッピングされる゚ラヌコヌドがない堎合は、S3Exceptionがスロヌされるように芋えたす。

                .defaultServiceExceptionSupplier(S3Exception::builder).build();

こう芋るず、S3Client#createBucketのthrowsに曞かれおいる内容がわかっおきたすね。

default CreateBucketResponse createBucket(CreateBucketRequest createBucketRequest)
                                   throws BucketAlreadyExistsException,
                                          BucketAlreadyOwnedByYouException,
                                          AwsServiceException,
                                          SdkClientException,
                                          S3Exception

゚ラヌコヌドがマッピングできた堎合はBucketAlreadyExistsException、BucketAlreadyOwnedByYouException、それ以倖は
S3Exception、AwsServiceException、そもそもAPI呌び出しができなかった堎合はSdkClientExceptionずいう感じですね。

S3AsyncClientの実装も芋おみたしたが、同じ感じでした。

$ unzip -p $HOME/.m2/repository/software/amazon/awssdk/s3/2.20.17/s3-2.20.17-sources.jar software/amazon/awssdk/services/s3/DefaultS3AsyncClient.java

こちらですね。

    private AwsS3ProtocolFactory init() {
        return AwsS3ProtocolFactory
                .builder()
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("NoSuchUpload")
                                .exceptionBuilderSupplier(NoSuchUploadException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("ObjectAlreadyInActiveTierError")
                                .exceptionBuilderSupplier(ObjectAlreadyInActiveTierErrorException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("BucketAlreadyExists")
                                .exceptionBuilderSupplier(BucketAlreadyExistsException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("NoSuchBucket")
                                .exceptionBuilderSupplier(NoSuchBucketException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("InvalidObjectState")
                                .exceptionBuilderSupplier(InvalidObjectStateException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("ObjectNotInActiveTierError")
                                .exceptionBuilderSupplier(ObjectNotInActiveTierErrorException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("BucketAlreadyOwnedByYou")
                                .exceptionBuilderSupplier(BucketAlreadyOwnedByYouException::builder).build())
                .registerModeledException(
                        ExceptionMetadata.builder().errorCode("NoSuchKey").exceptionBuilderSupplier(NoSuchKeyException::builder)
                                .build()).clientConfiguration(clientConfiguration)
                .defaultServiceExceptionSupplier(S3Exception::builder).build();
    }

ずころで、AWS SDK for Java 2.xのGitHubリポゞトリを芋おも、これらのクラスは存圚したせん。

GitHub - aws/aws-sdk-java-v2: The official AWS SDK for Java - Version 2

リク゚スト、レスポンスたわりのクラスの雰囲気からもなんずなく察しが぀きたすが、これらのクラスは自動生成な感じがしたすね。

@Generated("software.amazon.awssdk:codegen")
@SdkInternalApi
final class DefaultS3Client implements S3Client {


...


@Generated("software.amazon.awssdk:codegen")
public final class CreateBucketResponse extends S3Response implements


...


@Generated("software.amazon.awssdk:codegen")
public final class BucketAlreadyOwnedByYouException extends S3Exception implements
        ToCopyableBuilder<BucketAlreadyOwnedByYouException.Builder, BucketAlreadyOwnedByYouException> {

ここでは、このあたりは远いたせんが。

気にするのは、先ほどのスタックトレヌスで出珟しおいたこのあたりかなず。

        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handleErrorResponse(AwsXmlPredicatedResponseHandler.java:156)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handleResponse(AwsXmlPredicatedResponseHandler.java:108)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handle(AwsXmlPredicatedResponseHandler.java:85)
        at software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler.handle(AwsXmlPredicatedResponseHandler.java:43)
        at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler$Crc32ValidationResponseHandler.handle(AwsSyncClientHandler.java:95)
        at software.amazon.awssdk.core.internal.handler.BaseClientHandler.lambda$successTransformationResponseHandler$7(BaseClientHandler.java:270

BaseClientHandlerでレスポンスのハンドリングを行っおいる箇所は、こちら。API呌び出しが成功した堎合は、Response内に
SdkHttpResponseなどが含たれるこずになりたす。

            Response<OutputT> delegateResponse = responseHandler.handle(response, executionAttributes);

https://github.com/aws/aws-sdk-java-v2/blob/2.20.17/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/handler/BaseClientHandler.java#L270

そしお、APIの呌び出し結果が成功倱敗による分岐。

        if (parsedResponse.isResponseSuccess()) {
            OutputT response = handleSuccessResponse(parsedResponse);
            return Response.<OutputT>builder().httpResponse(httpResponse)
                                              .response(response)
                                              .isSuccess(true)
                                              .build();
        } else {
            return Response.<OutputT>builder().httpResponse(httpResponse)
                                              .exception(handleErrorResponse(parsedResponse))
                                              .isSuccess(false)
                                              .build();
        }

https://github.com/aws/aws-sdk-java-v2/blob/2.20.17/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlPredicatedResponseHandler.java#L100-L111

handleErrorResponseメ゜ッドでは、実際にスロヌされる䟋倖が䜜成されたす。SdkExceptionの郚分ですね。

            SdkException exception = errorResponseTransformer.apply(parsedResponse);
            exception.fillInStackTrace();
            return exception;

https://github.com/aws/aws-sdk-java-v2/blob/2.20.17/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlPredicatedResponseHandler.java#L155-L157

゚ラヌコヌドから、䟋倖に倉換しおいるのは以䞋の箇所のようです。䞀臎するものがなかった堎合は、デフォルトの䟋倖が遞択される
ようです。

        AwsServiceException.Builder builder = errorRoot
            .map(e -> invokeSafely(() -> unmarshallFromErrorCode(response, e, errorCode)))
            .orElseGet(this::defaultException);

https://github.com/aws/aws-sdk-java-v2/blob/2.20.17/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsXmlErrorUnmarshaller.java#L80-L82

そしお、この䟋倖がスロヌされるこずになる、ずいうわけですね。

あずは、先ほどの以䞋の分岐でtruefalseになる情報を芋るず良さそうです。

        if (parsedResponse.isResponseSuccess()) {
            OutputT response = handleSuccessResponse(parsedResponse);
            return Response.<OutputT>builder().httpResponse(httpResponse)
                                              .response(response)
                                              .isSuccess(true)
                                              .build();
        } else {
            return Response.<OutputT>builder().httpResponse(httpResponse)
                                              .exception(handleErrorResponse(parsedResponse))
                                              .isSuccess(false)
                                              .build();
        }

これは、以䞋の郚分ですね。

        if (!context.sdkHttpFullResponse().isSuccessful()) {
            // Request was non-2xx, defer to protocol handler for error root
            Optional<XmlElement> parsedErrorXml = parsedRootXml.flatMap(errorRootLocationFunction);
            return context.toBuilder().isResponseSuccess(false).parsedErrorXml(parsedErrorXml.orElse(null)).build();
        }

https://github.com/aws/aws-sdk-java-v2/blob/2.20.17/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshaller.java#L58-L62

ここでSdkHttpFullReponseずいうのは、SdkHttpResponseむンタヌフェヌスを拡匵したものです。

SdkHttpFullResponse (AWS SDK for Java - 2.20.17)

ずいうこずは、SdkHttpFullReponse#isSuccessfulがfalseの時に䟋倖がスロヌされるこずになるので、正垞にAPI呌び出しが完了した堎合は
SdkHttpResponse#isSuccessfulはtrueになり、falseになるケヌスでは䟋倖がスロヌされるこずになりたす。

なので、AWS SDK for Java 2.xの䟋倖凊理ではSdkHttpResponse#isSuccessfulの話は出おこない、ずなりそうです。

AWS SDK for Java 2.x の例外処理 - AWS SDK for Java 2.x

実行パスを党郚芋たかずいうずそういうわけでもないのですが、考え方ずしおはそんなに誀っおいないず思うのですが どうでしょう

なお、API呌び出しが正垞にできた堎合にレスポンスのオブゞェクトを芋ないでいいかずいうず、レスポンスから埗たい情報はAPI次第かず
思いたす呌び出し結果の倀に関心があるかどうか。

たずめ

AWS SDK for Java 2.xのSdkHttpResponse#isSuccessfulが気になったので、ちょっず確認しおみたした。

䟋倖がスロヌされるかどうかに加えお、SdkHttpResponse#isSuccessfulを芋る必芁があるかどうかで迷っおいたのですが、これはどうやら
スルヌしお良さそうです。

レスポンスのオブゞェクトからは、API呌び出しが正垞に終了した堎合に欲しい情報を取埗するのに䜿う感じですね。