

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# SDK を使用する
<a name="using"></a>

このセクションでは、 を使用するために必要な基本情報を提供します AWS SDK for Kotlin。

**Topics**
+ [リクエストを発行する](making-requests.md)
+ [コルーチン](coroutines.md)
+ [ストリーミング操作](streaming-ops.md)
+ [ページ分割](pagination.md)
+ [ウェーター](waiters.md)
+ [エラー処理](error-handling.md)
+ [事前署名リクエスト](presign-requests.md)
+ [トラブルシューティングに関するよくある質問](troubleshooting-faqs.md)
+ [でのモック AWS SDK for Kotlin](mocking.md)

# リクエストを発行する
<a name="making-requests"></a>

サービスクライアントを使用して にリクエストを行います AWS のサービス。 AWS SDK for Kotlin は、リクエストを作成するための[型セーフビルダー](https://kotlinlang.org/docs/type-safe-builders.html)パターンに従ってドメイン固有の言語 (DSLs) を提供します。ネストされたリクエスト構造には、DSLs からもアクセスできます。

次の例は、Amazon DynamoDB [createTable](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb/aws.sdk.kotlin.services.dynamodb/create-table.html) オペレーション入力を作成する方法を示しています。

```
val ddb = DynamoDbClient.fromEnvironment()

val req = CreateTableRequest {
    tableName = name
    keySchema = listOf(
        KeySchemaElement {
            attributeName = "year"
            keyType = KeyType.Hash
        },
        KeySchemaElement {
            attributeName = "title"
            keyType = KeyType.Range
        }
    )

    attributeDefinitions = listOf(
        AttributeDefinition {
            attributeName = "year"
            attributeType = ScalarAttributeType.N
        },
        AttributeDefinition {
            attributeName = "title"
            attributeType = ScalarAttributeType.S
        }
    )
    
    // You can configure the `provisionedThroughput` member
    // by using the `ProvisionedThroughput.Builder` directly:
    provisionedThroughput {
        readCapacityUnits = 10
        writeCapacityUnits = 10
    }
}

val resp = ddb.createTable(req)
```

## サービスインターフェイスの DSL オーバーロード
<a name="service-interface-dsl-overloads"></a>

サービスクライアントインターフェイスの各非ストリーミングオペレーションには DSL オーバーロードがあるため、個別のリクエストを作成する必要はありません。

オーバーロードされた関数を使用して Amazon Simple Storage Service (Amazon S3) バケットを作成する例:

```
s3Client.createBucket {    // this: CreateBucketRequest.Builder
    bucket = newBucketName
}
```

これは次に相当します:

```
val request = CreateBucketRequest {    // this: CreateBucketRequest.Builder 
    bucket = newBucketName 
}

s3client.createBucket(request)
```

## 入力を必要としないリクエスト
<a name="requests-no-required-inputs"></a>

必要な入力がないオペレーションは、リクエストオブジェクトを渡すことなく呼び出すことができます。これは多くの場合、Amazon S3 API オペレーションなどのリストタイプのオペレーションで可能です。 `listBuckets`

 たとえば、次の 3 つのステートメントは同等です。

```
s3Client.listBuckets(ListBucketsRequest {
  // Construct the request object directly.
})
s3Client.listBuckets {
  // DSL builder without explicitly setting any arguments.
}
s3Client.listBuckets()
```

# コルーチン
<a name="coroutines"></a>

 AWS SDK for Kotlin はデフォルトで非同期です。SDK for Kotlin は、コルーチンから呼び出されるすべてのオペレーションに `suspend`関数を使用します。

コルーチンの詳細なガイドについては、[公式の Kotlin ドキュメント](https://kotlinlang.org/docs/coroutines-overview.html)を参照してください。

## 同時リクエストの実行
<a name="making-concurrent-requests"></a>

[非同期](https://kotlinlang.org/docs/composing-suspending-functions.html#concurrent-using-async)コルーチンビルダーを使用して、結果を重視する同時リクエストを起動できます。 は、後で結果を提供する promise を表す軽量でノンブロッキングの未来を表す [Deferred](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html) `async`を返します。

結果に関心がない場合 ( オペレーションが完了した場合のみ）、[起動](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html)コルーチンビルダーを使用できます。 `launch`は概念的に に似ています`async`。違いは、起動が[ジョブ](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html)を返し、結果の値は持たず、 は を`async`返すことです`Deferred`。

以下は、[headObject](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3/head-object.html) オペレーションを使用して Amazon S3 への同時リクエストを行い、2 つのキーのコンテンツサイズを取得する例です。

```
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis
import aws.sdk.kotlin.services.s3.S3Client


fun main(): Unit = runBlocking {

    val s3 = S3Client { region = "us-east-2" }
    
    val myBucket = "<your-bucket-name-here>"
    val key1 = "<your-object-key-here>"
    val key2 = "<your-second-object-key-here>"

    val resp1 = async {
        s3.headObject{
            bucket = myBucket
            key = key1
        }
    }

    val resp2 = async {
        s3.headObject{
            bucket = myBucket
            key = key2
        }
    }


    val elapsed = measureTimeMillis {
        val totalContentSize = resp1.await().contentLength + resp2.await().contentLength
        println("content length of $key1 + $key2 = $totalContentSize")
    }

    println("requests completed in $elapsed ms")

}
```

## ブロックリクエストの実行
<a name="making-clocking-requests"></a>

コルーチンを使用せず、別のスレッドモデルを実装する既存のコードからサービス呼び出しを行うには、[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) コルーチンビルダーを使用できます。別のスレッドモデルの例としては、Java の従来のエグゼキュター/未来アプローチの使用が挙げられます。Java と Kotlin のコードまたはライブラリをブレンドする場合は、このアプローチを使用する必要がある場合があります。

名前が示すように、この`runBlocking`ビルダーは新しいコルーチンを起動し、完了するまで現在のスレッドをブロックします。

**警告**  
 `runBlocking` 通常、コルーチンからは使用しないでください。これは、通常のブロッキングコードを一時停止スタイルで記述されたライブラリ (主要な関数やテストなど) にブリッジするように設計されています。

# ストリーミング操作
<a name="streaming-ops"></a>

では AWS SDK for Kotlin、バイナリデータ (ストリーム) は [https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.content/-byte-stream/index.html](https://docs.aws.amazon.com/smithy-kotlin/api/latest/runtime-core/aws.smithy.kotlin.runtime.content/-byte-stream/index.html)型として表されます。これはバイトの抽象読み取り専用ストリームです。

## ストリーミングレスポンス
<a name="streaming-responses"></a>

バイナリストリーム (Amazon Simple Storage Service (Amazon S3) [GetObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) API オペレーションなど) を使用したレスポンスは、他の方法とは異なる方法で処理されます。これらのメソッドは、レスポンスを直接返すのではなく、レスポンスを処理する Lambda 関数を使用します。これにより、関数へのレスポンスの範囲が制限され、呼び出し元と SDK ランタイムの両方のライフタイム管理が簡素化されます。

Lambda 関数が戻ると、基盤となる HTTP 接続などのリソースが解放されます。(Lambda が戻った後は にアクセス`ByteStream`しないでください。また、閉鎖から渡さないでください）。呼び出しの結果は、Lambda が返すものです。

次のコード例は、レスポンスを処理する Lambda パラメータを受信する [getObject](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3/-s3-client/get-object.html) 関数を示しています。

```
val s3Client = S3Client.fromEnvironment()
val req = GetObjectRequest { ... }

val path = Paths.get("/tmp/download.txt")

// S3Client.getObject has the following signature:
// suspend fun <T> getObject(input: GetObjectRequest, block: suspend (GetObjectResponse) -> T): T

val contentSize = s3Client.getObject(req) { resp ->
    // resp is valid until the end of the block.
    // Do not attempt to store or process the stream after the block returns.
    
    // resp.body is of type ByteStream.
    val rc = resp.body?.writeToFile(path)
    rc
}
println("wrote $contentSize bytes to $path")
```

`ByteStream` タイプには、一般的な使用方法として次の拡張機能があります。
+ `ByteStream.writeToFile(file: File): Long`
+ `ByteStream.writeToFile(path: Path): Long`
+ `ByteStream.toByteArray(): ByteArray`
+ `ByteStream.decodeToString(): String`

これらはすべて `aws.smithy.kotlin.runtime.content`パッケージで定義されています。

## ストリーミングリクエスト
<a name="streaming-requests"></a>

を指定するには`ByteStream`、次のような便利な方法もあります。
+ `ByteStream.fromFile(file: File)`
+ `File.asByteStream(): ByteStream`
+ `Path.asByteStream(): ByteStream`
+ `ByteStream.fromBytes(bytes: ByteArray)`
+ `ByteStream.fromString(str: String)`

これらはすべて `aws.smithy.kotlin.runtime.content`パッケージで定義されています。

次のコード例は、[PutObjectRequest](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.model/-put-object-request/index.html) の作成時に本文プロパティを提供する`ByteStream`便利なメソッドの使用を示しています。

```
val req = PutObjectRequest {
    ...
    body = ByteStream.fromFile(file)
    // body = ByteStream.fromBytes(byteArray)
    // body = ByteStream.fromString("string")
    // etc
}
```

# ページ分割
<a name="pagination"></a>

多くの AWS オペレーションは、ペイロードが大きすぎて 1 回のレスポンスで返せない場合にページ分割された結果を返します。 AWS SDK for Kotlin には[、結果を自動的にページ分割するサービスクライアントインターフェイスの拡張機能](https://kotlinlang.org/docs/extensions.html)が含まれています。ユーザーは、この結果を処理するコードを記述するだけで済みます。

ページ分割は [Flow<T>](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/) として公開されるため、非同期コレクション (`map`、、 など) に対して Kotlin のイディオマティック変換を活用できます`filter``take`。例外は透過的であるため、エラー処理は通常の API コールのように感じられ、キャンセルはコルーチンの一般的な共同キャンセルに従います。詳細については、公式ガイドの[「フロー](https://kotlinlang.org/docs/flow.html)と[フロー例外](https://kotlinlang.org/docs/flow.html#flow-exceptions)」を参照してください。

**注記**  
次の例ではAmazon S3を使用しています。ただし、ページ分割された API を 1 つ以上持つサービスについては同じ概念です。すべてのページ分割拡張機能は、 `aws.sdk.kotlin.services.<service>.paginators`パッケージ ( など) で定義されます`aws.sdk.kotlin.services.dynamodb.paginators`。

次のコード例は、[listObjectsV2Paginated](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.paginators/list-objects-v2-paginated.html) 関数呼び出しからページ分割されたレスポンスを処理する方法を示しています。

**インポート**

```
import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.paginators.listObjectsV2Paginated
import kotlinx.coroutines.flow.*
```

**コード**

```
val s3 = S3Client.fromEnvironment()
val req = ListObjectsV2Request {
    bucket = "amzn-s3-demo-bucket"
    maxKeys = 1
}

s3.listObjectsV2Paginated(req)  // Flow<ListObjectsV2Response>
    .transform { it.contents?.forEach { obj -> emit(obj) } }
    .collect { obj ->
        println("key: ${obj.key}; size: ${obj.size}")
    }
```

# ウェーター
<a name="waiters"></a>

ウェイターは、クライアント側の抽象化で、リソースが目的の状態に達するまで、またはそのリソースが目的の状態に入らないと判断されるまで、リソースをポーリングします。これは、Amazon Simple Storage Service (Amazon S3) などの結果整合性のあるサービスや、Amazon EC2 などのリソースを非同期的に作成するサービスを使用する場合の一般的なタスクです。

リソースのステータスを定期的にポーリングするロジックを自分で記述するのは、手間がかかりエラーの原因にもなります。ウェーターの目標は、この責任を顧客コードから に移行することです。この には AWS SDK for Kotlin、 AWS オペレーションのタイミングの側面に関する深い知識があります。

**注記**  
次の例ではAmazon S3を使用しています。ただし、1 つ以上のウェイターが定義されている AWS のサービス については、概念は同じです。すべての拡張機能は`aws.sdk.kotlin.services.<service>.waiters`パッケージで定義されます ( など`aws.sdk.kotlin.services.dynamodb.waiters`)。また、標準の命名規則 () に従います`waitUntil<Condition>`。

次のコード例は、ポーリングロジックの記述を回避できるウェーター関数の使用を示しています。

**インポート**

```
import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.waiters.waitUntilBucketExists
```

**コード**

```
val s3 = S3Client.fromEnvironment()

// This initiates creating an S3 bucket and potentially returns before the bucket exists.
s3.createBucket { bucket = "amzn-s3-demo-bucket" }

// When this function returns, the bucket either exists or an exception
// is thrown.
s3.waitUntilBucketExists { bucket = "amzn-s3-demo-bucket" }

// The bucket now exists.
```

**注記**  
各待機メソッドは、目的の条件に達することに対応する最終レスポンスで取得するために使用できる`Outcome`インスタンスを返します。結果には、目的の状態に到達しようとした回数などの追加の詳細も含まれます。

# エラー処理
<a name="error-handling"></a>

が例外を AWS SDK for Kotlin スローする方法とタイミングを理解することは、 SDK を使用して高品質のアプリケーションを構築する上で重要です。以下のセクションでは、SDK によってスローされる例外のさまざまなケース、および例外の適切な処理方法について説明します。

## サービス例外
<a name="service-exceptions"></a>

最も一般的な例外は で`AwsServiceException`、サービス固有の例外 ( など`S3Exception`) はすべて継承されます。この例外は、 AWS のサービスからのエラーレスポンスを表します。たとえば、存在しない Amazon EC2 インスタンスを終了しようとすると、Amazon EC2 はエラーレスポンスを返します。エラーレスポンスの詳細は、スロー`AwsServiceException`される に含まれています。

が発生すると`AwsServiceException`、リクエストは に正常に送信されました AWS のサービス が、処理できませんでした。これは、リクエストのパラメータに含まれるエラーまたはサービス側の問題が原因です。

## クライアント例外
<a name="client-exceptions"></a>

`ClientException` は、 へのリクエストの送信中またはレスポンスの AWS SDK for Kotlin 解析 AWS 中に、クライアントコード内で問題が発生したことを示します AWS。`ClientException` は通常、 よりも重大`AwsServiceException`であり、クライアントが へのサービス呼び出しを処理できないという大きな問題があることを示します AWS のサービス。たとえば、 は、サービスからのレスポンスの解析に失敗`ClientException`した場合、 を AWS SDK for Kotlin スローします。

## エラーメタデータ
<a name="error-metadata"></a>

すべてのサービス例外とクライアント例外には `sdkErrorMetadata`プロパティがあります。これは、エラーに関する追加の詳細を取得するために使用できる型付きプロパティバッグです。

`AwsErrorMetadata` タイプには、いくつかの事前定義された拡張機能が直接存在します。これには、以下が含まれますが、これらに限定されません。
+ `sdkErrorMetadata.requestId` – 一意のリクエスト ID
+ `sdkErrorMetadata.errorMessage` – 人間が読み取れるメッセージ (通常は と一致しますが`Exception.message`、例外がサービスに不明な場合は詳細情報が含まれる場合があります)
+ `sdkErrorMetadata.protocolResponse` — raw プロトコルレスポンス

次の例は、エラーメタデータへのアクセスを示しています。

```
try {
    s3Client.listBuckets { ... }
} catch (ex: S3Exception) {
    val awsRequestId = ex.sdkErrorMetadata.requestId
    val httpResp = ex.sdkErrorMetadata.protocolResponse as? HttpResponse

    println("requestId was: $awsRequestId")
    println("http status code was: ${httpResp?.status}")
}
```

# 事前署名リクエスト
<a name="presign-requests"></a>

一部の AWS API オペレーションのリクエストに事前署名することで、別の発信者が自分の認証情報を提示することなく後でリクエストを使用できるようになります。

たとえば、Alice が Amazon Simple Storage Service (Amazon S3) オブジェクトにアクセスでき、Bob とオブジェクトアクセスを一時的に共有したいとします。Alice は、署名付き`GetObject`リクエストを生成して Bob と共有できるため、Alice の認証情報にアクセスすることなくオブジェクトをダウンロードできます。

## 事前署名の基本
<a name="presign-requests-basics"></a>

SDK for Kotlin は、リクエストに事前署名するための拡張メソッドをサービスクライアントに提供します。すべての署名付きリクエストには、署名付きリクエストが有効である期間を表す期間が必要です。期間が終了すると、署名付きリクエストは期限切れになり、実行されると認証エラーが発生します。

次のコードは、Amazon S3 の署名付き`GetObject`リクエストを作成する例を示しています。リクエストは作成後 24 時間有効です。

```
val s3 = S3Client.fromEnvironment()

val unsignedRequest = GetObjectRequest {
    bucket = "foo"
    key = "bar"
}

val presignedRequest = s3.presignGetObject(unsignedRequest, 24.hours)
```

[https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.presigners/presign-get-object.html](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.presigners/presign-get-object.html) 拡張メソッドは [https://docs.aws.amazon.com/smithy-kotlin/api/latest/http/aws.smithy.kotlin.runtime.http.request/-http-request/index.html](https://docs.aws.amazon.com/smithy-kotlin/api/latest/http/aws.smithy.kotlin.runtime.http.request/-http-request/index.html) オブジェクトを返します。リクエストオブジェクトには、オペレーションを呼び出すことができる署名付き URL が含まれています。別の発信者は、別のコードベースまたはプログラミング言語環境で URL (またはリクエスト全体) を使用できます。

署名付きリクエストを作成したら、HTTP クライアントを使用してリクエストを呼び出します。HTTP GET リクエストを呼び出す API は、HTTP クライアントによって異なります。次の例では、Kotlin [https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.net.-u-r-l/read-text.html](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.net.-u-r-l/read-text.html)メソッドを使用しています。

```
val objectContents = URL(presignedRequest.url.toString()).readText()
println(objectContents)
```

## 高度な署名前設定
<a name="presign-requests-conf-advanced"></a>

SDK では、リクエストに事前署名できる各メソッドにオーバーロードがあり、特定の署名者の実装や詳細な署名パラメータなどの高度な設定オプションを提供するために使用できます。

次の例は、CRT 署名者バリアントを使用し、将来の署名日を指定する Amazon S3 `GetObject`リクエストを示しています。

```
val s3 = S3Client.fromEnvironment()

val unsignedRequest = GetObjectRequest {
    bucket = "foo"
    key = "bar"
}

val presignedRequest = s3.presignGetObject(unsignedRequest, signer = CrtAwsSigner) {
    signingDate = Instant.now() + 24.hours
    expiresAfter = 8.hours
}
```

返される署名付きリクエストは 24 時間転送されており、それ以前は有効ではありません。その 8 時間後に有効期限が切れます。

## POST リクエストと PUT リクエストの事前署名
<a name="presign-requests-post-put"></a>

事前署名可能なオペレーションの多くは URL のみを必要とし、HTTP GET リクエストとして実行する必要があります。ただし、一部のオペレーションは本文を取り、場合によってはヘッダーとともに HTTP POST または HTTP PUT リクエストとして実行する必要があります。これらのリクエストの事前署名は GET リクエストの事前署名と同じですが、署名付きリクエストの呼び出しはより複雑です。

S3 `PutObject`リクエストに事前署名する例を次に示します。

```
val s3 = S3Client.fromEnvironment()

val unsignedRequest = PutObjectRequest {
    bucket = "foo"
    key = "bar"
}

val presignedRequest = s3.presignPutObject(unsignedRequest, 24.hours)
```

返される `HttpRequest`のメソッド値は `HttpMethod.PUT`で、HTTP リクエストの今後の呼び出しに含める必要がある URL とヘッダーが含まれています。このリクエストは、別のコードベースまたはプログラミング言語環境で実行できる発信者に渡すことができます。

署名付き POST または PUT リクエストを作成したら、HTTP クライアントを使用してリクエストを呼び出します。POST または PUT リクエスト URL を呼び出す API は、使用する HTTP クライアントによって異なります。次の例では、[OkHttp HTTP クライアント](https://square.github.io/okhttp)を使用し、 を含む本文が含まれています`Hello world`。

```
val putRequest = Request
    .Builder()
    .url(presignedRequest.url.toString())
    .apply {
        presignedRequest.headers.forEach { key, values ->
            header(key, values.joinToString(", "))
        }
    }
    .put("Hello world".toRequestBody())
    .build()

val response = okHttp.newCall(putRequest).execute()
```

## SDK が事前署名できるオペレーション
<a name="presign-ops-supported"></a>

SDK for Kotlin は現在、関連付けられた HTTP メソッドで呼び出す必要がある次の API オペレーションの事前署名をサポートしています。


| AWS のサービス | 運用 | SDK 拡張機能メソッド | HTTP メソッドを使用する | 
| --- | --- | --- | --- | 
| Amazon S3 | GetObject | [presignGetObject](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.presigners/presign-get-object.html) |  HTTP GET  | 
| Amazon S3 | PutObject | [presignPutObject](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.presigners/presign-put-object.html) |  HTTP PUT  | 
| Amazon S3 | UploadPart |  [presignUploadPart](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/s3/aws.sdk.kotlin.services.s3.presigners/presign-upload-part.html)  |  HTTP PUT  | 
| AWS Security Token Service | GetCallerIdentity |  [presignGetCallerIdentity](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/sts/aws.sdk.kotlin.services.sts.presigners/presign-get-caller-identity.html)  |  HTTP POST  | 
| Amazon Polly | SynthesizeSpeech |  [presignSynthesizeSpeech](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/polly/aws.sdk.kotlin.services.polly.presigners/presign-synthesize-speech.html)  |  HTTP POST  | 

# トラブルシューティングに関するよくある質問
<a name="troubleshooting-faqs"></a>

 AWS SDK for Kotlin アプリケーションで を使用すると、このトピックに記載されているいくつかの問題が発生する可能性があります。根本原因を特定し、エラーを解決するには、次の提案を参考にしてください。

## 「接続が閉じられた」問題を修正するにはどうすればよいですか?
<a name="ts-faq-connection-closed"></a>

次のいずれかのタイプなどの例外として、「接続が閉じられた」問題が発生する可能性があります。
+ `IOException: unexpected end of stream on <URL>`
+ `EOFException: \n not found: limit=0`
+ `HttpException: AWS_ERROR_HTTP_CONNECTION_CLOSED: The connection has closed or is closing.; crtErrorCode=2058; HttpErrorCode(CONNECTION_CLOSED)`

これらの例外は、SDK からサービスへの TCP 接続が予期せず閉じられたか、リセットされたことを示します。接続は、ホスト、 AWS サービス、または NAT ゲートウェイ、プロキシ、ロードバランサーなどの仲介者によって閉じられた可能性があります。

これらのタイプの例外は自動的に再試行されますが、ログ記録設定によっては SDK ログに表示される場合があります。例外がコードにスローされた場合、アクティブな再試行戦略が最大試行回数や再試行トークンバケットなど、設定された制限を使い果たしたことを示します。再試行戦略の詳細については、このガイドの[で再試行する AWS SDK for Kotlin](retries.md)「」セクションを参照してください。[最大試行回数に達する前に例外がスローされるのはなぜですか?](#ts-faq-exceptions-before-max) も参照してください。

### OkHttpEngine によるアイドル接続のモニタリング
<a name="ts-faq-connection-closed-okhttp"></a>

を使用して`OkHttpEngine`いて、`IOException: unexpected end of stream on <URL>`例外が頻繁に発生する場合は、[アイドル状態の接続モニタリングを有効にすることを検討](http-client-config.md#http-idle-connection-monitoring)してください。この機能は、リモートサーバーがまだ接続プールにある接続を閉じているかどうかを検出するため、これらの例外の発生を減らすことができます。

## 最大試行回数に達する前に例外がスローされるのはなぜですか?
<a name="ts-faq-exceptions-before-max"></a>

再試行が予想されたが、代わりにスローされた例外が表示される場合があります。このような状況では、次の手順が問題の解決に役立つ場合があります。
+ **例外が再試行可能であることを確認します。**例えば、不正な形式のサービスリクエスト、アクセス許可の欠如、存在しないリソースを示す例外など、一部の例外は再試行できません。SDK は、これらの種類の例外を自動的に再試行しません。再試行可能な例外を確認する方法については、「」を参照してください[例外が再試行可能かどうかを確認します。](retries.md#retries-check-exception-retryable)。
+ **例外がコードにスローされていることを確認します。**一部の例外はログメッセージに情報として表示されますが、実際にはコードにスローされません。例えば、スロットリングエラーなどの再試行可能な例外は、SDK が複数のbackoff-and-retryサイクルを自動的に実行するとログに記録される場合があります。SDK オペレーションの呼び出しは、設定された再試行設定で処理されなかった場合にのみ例外をスローします。
+ **設定した再試行設定を確認します。**再試行戦略と再試行ポリシーの詳細については、このガイドの[で再試行する AWS SDK for Kotlin](retries.md)「」セクションを参照してください。コードが想定した設定または自動デフォルトを使用していることを確認します。
+ **再試行設定の調整を検討してください。**前の項目を検証しても問題が解決しない場合は、再試行設定の調整を検討してください。
  + **最大試行回数を増やします。**デフォルトでは、 オペレーションの最大試行回数は 3 回です。これが十分ではなく、デフォルト設定で例外が発生している場合は、クライアント設定で `retryStrategy.maxAttempts`プロパティを増やすことを検討してください。詳細については「[最大試行回数を設定する](retries.md#retires-max-attempts)」を参照してください。
  + **遅延設定を増やします。**一部の例外は、基盤となる条件が解決される前に、あまりにも迅速に再試行される可能性があります。その場合は、クライアント設定で `retryStrategy.delayProvider.initialDelay`または `retryStrategy.delayProvider.maxBackoff`プロパティを増やすことを検討してください。詳細については「[遅延とバックオフを設定する](retries.md#retries-delays-backoff)」を参照してください。
  + **サーキットブレーカーモードを無効にします。**SDK は、デフォルトで各サービスクライアントのトークンのバケットを維持します。SDK がリクエストを試行し、再試行可能な例外で失敗すると、トークン数は減少し、リクエストが成功すると、トークン数は増加します。

    デフォルトでは、このトークンバケットが残り 0 トークンに達すると、回路が壊れます。回路が切断されると、SDK は再試行を無効にし、最初の試行で失敗した現在および後続のリクエストは直ちに例外をスローします。SDK は、最初の試行が成功するとトークンバケットに十分な容量を返した後、再試行を再度有効にします。この動作は意図的なものであり、サービス停止やサービス復旧中の再試行ストームを防ぐように設計されています。

    SDK が設定された最大試行回数まで再試行し続ける場合は、クライアント設定で `retryStrategy.tokenBucket.useCircuitBreakerMode`プロパティを false に設定して、サーキットブレーカーモードを無効にすることを検討してください。このプロパティを false に設定すると、SDK クライアントはトークンバケットが十分な容量に達するまで待機します。残りのトークンが 0 個ある場合、例外につながる可能性のある追加の再試行は中止しません。

## `NoSuchMethodError` または NoClassDefFoundError を修正するにはどうすればよいですか?
<a name="ts-faq-nusuchmethod"></a>

これらのエラーは、依存関係の欠落または競合によって最も一般的に発生します。詳細については「[依存関係の競合を解決するにはどうすればよいですか?](ts-faq-dep-conflict-resolution.md)」を参照してください。

### `NoClassDefFoundError` の が表示されます `okhttp3/coroutines/ExecuteAsyncKt`
<a name="ts-faq-nusuchmethod-okhttp"></a>

これは、特に OkHttp の依存関係の問題を示します。詳細については「[アプリケーションでの OkHttp バージョンの競合の解決](ts-faq-dep-conflict-resolution.md#okhttp-dep-conflicts)」を参照してください。

# 依存関係の競合を解決するにはどうすればよいですか?
<a name="ts-faq-dep-conflict-resolution"></a>

を使用する場合は AWS SDK for Kotlin、特定の AWS およびサードパーティーの依存関係が正しく動作する必要があります。これらの依存関係が実行時に欠落している、または予期しないバージョンがある場合、 `NoSuchMethodError`や などのエラーが表示されることがあります`NoClassDefFoundError`。これらの依存関係の問題は、通常 2 つのグループに分類されます。
+ SDK/Smithy 依存関係の競合
+ サードパーティーの依存関係の競合

Kotlin アプリケーションを構築するときは、Gradle を使用して依存関係を管理する可能性があります。SDK サービスクライアントへの依存関係をプロジェクトに追加すると、関連するすべての依存関係が自動的に含まれます。ただし、アプリケーションに他の依存関係がある場合、SDK で必要な依存関係と競合する可能性があります。例えば、SDK は、アプリケーションが使用する一般的な HTTP クライアントである OkHttp に依存しています。これらの競合を見つけるために、Gradle はプロジェクトの依存関係を一覧表示する便利なタスクを提供します。

```
./gradlew dependencies
```

依存関係の競合が発生した場合は、アクションを実行する必要がある場合があります。特定のバージョンの依存関係またはシャドウ依存関係をローカル名前空間に指定できます。Gradle 依存関係の解決は、*Gradle ユーザーマニュアル*の以下のセクションで説明する複雑なトピックです。
+ [ 依存関係の解決について ](https://docs.gradle.org/current/userguide/dependency_resolution.html)
+ [ 依存関係の制約と競合の解決 ](https://docs.gradle.org/current/userguide/dependency_constraints_conflicts.html)
+ [ 依存関係バージョンの調整 ](https://docs.gradle.org/current/userguide/dependency_version_alignment.html)

## プロジェクトの SDK と Smithy の依存関係の管理
<a name="sdk-smithy-dep-conflicts"></a>

SDK を使用する場合は、そのモジュールは通常、バージョン番号が一致する他の SDK モジュールに依存することに注意してください。たとえば、 `aws.sdk.kotlin:s3:1.2.3`は に依存し`ws.sdk.kotlin:aws-http:1.2.3`、 は に依存し`aws.sdk.kotlin:aws-core:1.2.3`ます。

SDK モジュールは、特定の Smithy モジュールバージョンも使用します。Smithy モジュールバージョンは SDK バージョン番号と同期しませんが、SDK の想定バージョンと一致する必要があります。たとえば、 `aws.sdk.kotlin:s3:1.2.3`は に依存し`aws.smithy.kotlin:serde:1.1.1`、 は に依存し`aws.smithy.kotlin:runtime-core:1.1.1`ます。

依存関係の競合を回避するには、すべての SDK 依存関係を一緒にアップグレードし、明示的な Smithy 依存関係に対して同じ操作を行います。[Gradle バージョンカタログ](setup-create-project-file.md)を使用してバージョンを同期させ、SDK と Smithy バージョン間のマッピングにおける推測を排除することを検討してください。

SDK/Smithy モジュールのマイナーバージョン更新には、[バージョニングポリシー](https://github.com/awslabs/aws-sdk-kotlin/blob/main/VERSIONING.md#versioning-policy)で説明されているように、重大な変更が含まれる場合があることに注意してください。マイナーバージョン間でアップグレードする場合は、変更ログを慎重に確認し、ランタイム動作を徹底的にテストしてください。

## アプリケーションでの OkHttp バージョンの競合の解決
<a name="okhttp-dep-conflicts"></a>

[OkHttp](https://square.github.io/okhttp/) は、SDK が JVM でデフォルトで使用する一般的な HTTP エンジンです。アプリケーションには、別の OkHttp バージョンを取り込む他の依存関係やフレームワークが含まれる場合があります。これにより、 `okhttp/coroutines/ExecuteAsyncKt` や など、`okhttp3`名前空間内の`NoClassDefFoundError`クラスの が発生する可能性があります`okhttp3/ConnectionListener`。この場合、通常、新しいバージョンを選択して競合を解決する必要があります。これらの競合の原因を追跡しやすくするために、Gradle には便利なタスクが用意されています。以下を実行して、すべての依存関係を一覧表示できます。

```
./gradlew dependencies
```

たとえば、SDK が OkHttp に依存し`5.0.0-alpha.14`、Spring Boot などの別の依存関係が OkHttp に依存している場合は`4.12.0`、 を使用する必要があります`5.0.0-alpha.14 version`。これを行うには、Gradle の `constraints`ブロックを使用します。

```
dependencies {
    constraints {
        implementation("com.squareup.okhttp3:okhttp:4.12.0")
    }
}
```

または、OkHttp 4.x を使用する必要がある場合、SDK は を提供します`OkHttp4Engine`。Gradle を設定してコード`OkHttp4Engine`で使用する方法については、 [ドキュメント](https://github.com/smithy-lang/smithy-kotlin/tree/main/runtime/protocol/http-client-engines/http-client-engine-okhttp4)を参照してください。

# でのモック AWS SDK for Kotlin
<a name="mocking"></a>

開発者は、複数のフレームワークを使用して、 を使用したテストでモックを実行できます AWS SDK for Kotlin。このトピックでは、一部のフレームワークに必要な追加の設定や特別な考慮事項について説明します。

## MockK
<a name="mockk-consideration"></a>

[MockK](https://mockk.io/) を使用してモジュール全体の拡張機能をモックする場合は、[追加の設定](https://mockk.io/#extension-functions)を行う必要があります。SDK for Kotlin では、ページネーター、ウェーター、およびプレシグナーが拡張関数の例であるため、動作をモックするときは追加の設定が必要です。

モックを設定する`mockkStatic("<MODULE_CLASS_NAME>")`前に を呼び出す必要があります。原則として、モジュールクラス名は次のとおりです。
+ **ページネーター**: `aws.sdk.kotlin.services.<service>.paginators.PaginatorsKt`
+ **ウェーター**: `aws.sdk.kotlin.services.<service>.waiters.WaitersKt`
+ **署名者**: `aws.sdk.kotlin.services.<service>.presigners.PresignersKt`

たとえば、ページネーター拡張関数`listBucketsPaginated`であるモックを含む次のテストでは、 を追加します`mockkStatic("aws.sdk.kotlin.services.s3.paginators.PaginatorsKt")`。

```
    @Test
    fun testPaginatedListBuckets() = runTest {

        mockkStatic("aws.sdk.kotlin.services.s3.paginators.PaginatorsKt")
        val s3Client: S3Client = mockk()
        val s3BucketLister = S3BucketLister(s3Client)

        val expectedBuckets = listOf(
            Bucket { name = "bucket1" },
            Bucket { name = "bucket2" }
        )

        val response = ListBucketsResponse { buckets = expectedBuckets }
        coEvery { s3Client.listBucketsPaginated() } returns flowOf(response)

        val result = s3BucketLister.getAllBucketNames()

        assertEquals(listOf("bucket1", "bucket2"), result)
    }
```

を使用しない場合`mockkStatic`、次のエラーが表示されます。

```
Missing mocked calls inside every { ... } block: make sure the object inside the block is a mock
io.mockk.MockKException: Missing mocked calls inside every { ... } block: make sure the object inside the block is a mock
    at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
    at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
    at io.mockk.impl.recording.CommonCallRecorder.done(CommonCallRecorder.kt:47)
    at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:63)
    at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
    at io.mockk.MockKDsl.internalCoEvery(API.kt:100)
    at io.mockk.MockKKt.coEvery(MockK.kt:174)
```

を使用しないプレシグナー拡張関数の場合`mockkStatic`、以下が表示されることがあります。

```
key is bound to the URI and must not be null
java.lang.IllegalArgumentException: key is bound to the URI and must not be null
    at aws.sdk.kotlin.services.s3.serde.GetObjectOperationSerializer.serialize(GetObjectOperationSerializer.kt:26)
    at aws.sdk.kotlin.services.s3.presigners.PresignersKt.presignGetObject(Presigners.kt:49)
    at aws.sdk.kotlin.services.s3.presigners.PresignersKt.presignGetObject$default(Presigners.kt:40)
    at aws.sdk.kotlin.services.s3.presigners.PresignersKt.presignGetObject-exY8QGI(Presigners.kt:30)
```

### すべてのアーティファクトの例
<a name="mockk-full-example"></a>

#### テスト対象のコード
<a name="mockk-example-code-under-test"></a>

```
import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.paginators.listBucketsPaginated
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.runBlocking
import org.slf4j.Logger
import org.slf4j.LoggerFactory

fun main() {
    val logger: Logger = LoggerFactory.getLogger(::main.javaClass)

    // Create an S3Client
    S3Client { region = "us-east-1" }.use { s3Client ->
        // Create service instance
        val bucketLister = S3BucketLister(s3Client)
        // Since getAllBucketNames is a suspend function, you'll need to run it in a coroutine scope
        runBlocking {
            val bucketNames = bucketLister.getAllBucketNames()
            logger.info("Found buckets: $bucketNames")
        }
    }
}

class S3BucketLister(private val s3Client: S3Client) {
    suspend fun getAllBucketNames(): List<String> {
        return s3Client.listBucketsPaginated()
            .transform { response ->
                response.buckets?.forEach { bucket ->
                    emit(bucket.name ?: "")
                }
            }
            .filter { it.isNotEmpty() }
            .toList()
    }
}
```

#### テストクラス
<a name="mockk-example-test-code"></a>

```
import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.model.Bucket
import aws.sdk.kotlin.services.s3.model.ListBucketsResponse
import aws.sdk.kotlin.services.s3.paginators.listBucketsPaginated
import io.mockk.coEvery
import io.mockk.mockk
import io.mockk.mockkStatic
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class S3BucketListerTest {

    @Test
    fun testPaginatedListBuckets() = runTest {

        mockkStatic("aws.sdk.kotlin.services.s3.paginators.PaginatorsKt")
        val s3Client: S3Client = mockk()
        val s3BucketLister = S3BucketLister(s3Client)

        val expectedBuckets = listOf(
            Bucket { name = "bucket1" },
            Bucket { name = "bucket2" }
        )

        val response = ListBucketsResponse { buckets = expectedBuckets }
        coEvery { s3Client.listBucketsPaginated() } returns flowOf(response)

        val result = s3BucketLister.getAllBucketNames()

        assertEquals(listOf("bucket1", "bucket2"), result)
    }
}
```

#### build.gradle.kts
<a name="mockk-example-gradle-build"></a>

```
plugins {
    kotlin("jvm") version "2.1.20"
    application
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation(platform(awssdk.bom))
    implementation(platform("org.apache.logging.log4j:log4j-bom:2.24.3"))

    implementation(awssdk.services.s3)
    implementation("org.apache.logging.log4j:log4j-slf4j2-impl")

    // Testing Dependencies
    testImplementation(platform("org.junit:junit-bom:5.11.0"))
    testImplementation("org.junit.jupiter:junit-jupiter")
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
    testImplementation("io.mockk:mockk:1.14.0")
}

tasks.test {
    useJUnitPlatform()
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = "org.example.S3BucketService"
}
```

#### settings.gradle.kts
<a name="mockk-example-settings-build"></a>

```
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
}
rootProject.name = "mockK-static"

dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }

    versionCatalogs {
        create("awssdk") {
            from("aws.sdk.kotlin:version-catalog:1.4.69")
        }
    }
}
```