Repository: aws/aws-sdk-java-v2
Pull request #6001: Migration Tool - PutObjectRequest setters
Author: login: davidh44
Created: 2025-04-01T17:30:10Z | Merged: 2025-04-02T20:38:50Z

<!--- Provide a general summary of your changes in the Title above -->

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here -->
Adds support for S3 POJO transforms:
`ObjectTagging, AccessControlList`

Adds support for `PutObjectRequest` setters:
`redirectLocation, tagging`

Adds comments for `PutObjectRequest` unsupported setters:
`sseCustomerKey, sseAwsKeyManagementParams, accessControlList`

Adds type check for `ObjectMetadata` arg in `putObject(bucket, key, InputStream, ObjectMetadata)` , as user could pass in a non variable, e.g. empty `new ObjectMetadata()` or mock value `any(ObjectMetadata.class)`

## Testing
<!--- Please describe in detail how you tested your changes -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
Added end to end tests



Diff:
diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java
index f8a83e2e89bf..1e8941e06da6 100644
--- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java
+++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java
@@ -16,12 +16,15 @@
 package foo.bar;
 
 import com.amazonaws.HttpMethod;
+import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams;
+import com.amazonaws.services.s3.model.SSECustomerKey;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.Date;
 import software.amazon.awssdk.core.async.AsyncRequestBody;
 import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.AccessControlPolicy;
 import software.amazon.awssdk.services.s3.model.GeneratePresignedUrlRequest;
 import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
 import software.amazon.awssdk.services.s3.model.PutObjectRequest;
@@ -32,33 +35,49 @@ public class S3Transforms {
 
     void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String key) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndLiteralLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location").contentLength(333L)
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndLiteralLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, 333, newExecutorServiceVariableToDefine)).build());
     }
 
     void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String key) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
         long contentLen = 777;
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndAssignedLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location").contentLength(contentLen)
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndAssignedLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, contentLen, newExecutorServiceVariableToDefine)).build());
     }
 
     void upload_streamWithoutLength(S3TransferManager tm, String bucket, String key) {
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndNoLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location")
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndNoLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, -1L, newExecutorServiceVariableToDefine)).build());
     }
 
+    void putObjectRequest_unsupportedSetters() {
+        SSECustomerKey sseCustomerKey = new SSECustomerKey("val");
+        SSEAwsKeyManagementParams sseParams = new SSEAwsKeyManagementParams();
+        AccessControlPolicy accessControlList = AccessControlPolicy.builder()
+            .build();
+
+        PutObjectRequest request = /*AWS SDK for Java v2 migration: Transform for PutObjectRequest setter accessControlList is not supported, please manually migrate your code to use the v2 setters: acl, grantReadACP, grantWriteACP - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/PutObjectRequest.Builder.html#acl(java.lang.String)*/
+/*AWS SDK for Java v2 migration: Transform for PutObjectRequest setter sseCustomerKey is not supported, please manually migrate your code to use the v2 setters: sseCustomerKey, sseCustomerKeyMD5 - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/PutObjectRequest.Builder.html#sseCustomerKey(java.lang.String)*/
+/*AWS SDK for Java v2 migration: Transform for PutObjectRequest setter sseAwsKeyManagementParam is not supported, please manually migrate your code to use the v2 setters: ssekmsKeyId, serverSideEncryption, sseCustomerAlgorithm - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/PutObjectRequest.Builder.html#ssekmsKeyId(java.lang.String)*/
+PutObjectRequest.builder().bucket("bucket").key("key").websiteRedirectLocation("location")
+            .sseCustomerKey(sseCustomerKey)
+            .sseAwsKeyManagementParams(sseParams)
+            .accessControlList(accessControlList)
+            .build();
+    }
+
     void objectmetadata_unsupportedSetters(Date dateVal) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
 
         /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - expirationTimeRuleId - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.expirationTimeRuleId("expirationTimeRuleId");
         /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - ongoingRestore - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.ongoingRestore(false);
diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/before/src/main/java/foo/bar/S3Transforms.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/before/src/main/java/foo/bar/S3Transforms.java
index 1eac54b13882..1eeedfdda64d 100644
--- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/before/src/main/java/foo/bar/S3Transforms.java
+++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/before/src/main/java/foo/bar/S3Transforms.java
@@ -17,9 +17,12 @@
 
 import com.amazonaws.HttpMethod;
 import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.model.AccessControlList;
 import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
 import com.amazonaws.services.s3.model.ObjectMetadata;
 import com.amazonaws.services.s3.model.PutObjectRequest;
+import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams;
+import com.amazonaws.services.s3.model.SSECustomerKey;
 import com.amazonaws.services.s3.transfer.TransferManager;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -54,6 +57,17 @@ void upload_streamWithoutLength(TransferManager tm, String bucket, String key) {
         tm.upload(requestWithStreamAndNoLength);
     }
 
+    void putObjectRequest_unsupportedSetters() {
+        SSECustomerKey sseCustomerKey = new SSECustomerKey("val");
+        SSEAwsKeyManagementParams sseParams = new SSEAwsKeyManagementParams();
+        AccessControlList accessControlList = new AccessControlList();
+
+        PutObjectRequest request = new PutObjectRequest("bucket", "key", "location")
+            .withSSECustomerKey(sseCustomerKey)
+            .withSSEAwsKeyManagementParams(sseParams)
+            .withAccessControlList(accessControlList);
+    }
+
     void objectmetadata_unsupportedSetters(Date dateVal) {
         ObjectMetadata metadata = new ObjectMetadata();
 
diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3.java
index 6fd807056ba2..ff3bd49cbcc2 100644
--- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3.java
+++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3.java
@@ -409,22 +409,26 @@ private void s3Uri(URI uri, String uriAsString) {
     }
 
     private void generatePresignedUrl(S3Client s3, String bucket, String key, Date expiration) {
-        URL urlGet1 = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build()
+        URL urlGet1 = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/
+            S3Presigner.builder().s3Client(s3).build()
                 .presignGetObject(p -> p.getObjectRequest(r -> r.bucket(bucket).key(key))
                     .signatureDuration(Duration.between(Instant.now(), expiration.toInstant())))
                 .url();
 
-        URL urlPut = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build()
+        URL urlPut = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/
+            S3Presigner.builder().s3Client(s3).build()
                 .presignPutObject(p -> p.putObjectRequest(r -> r.bucket(bucket).key(key))
                     .signatureDuration(Duration.between(Instant.now(), expiration.toInstant())))
                 .url();
 
-        URL urlGet2 = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build()
+        URL urlGet2 = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/
+            S3Presigner.builder().s3Client(s3).build()
                 .presignGetObject(p -> p.getObjectRequest(r -> r.bucket(bucket).key(key))
                     .signatureDuration(Duration.between(Instant.now(), expiration.toInstant())))
                 .url();
 
-        URL urlDelete = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build()
+        URL urlDelete = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/
+            S3Presigner.builder().s3Client(s3).build()
                 .presignDeleteObject(p -> p.deleteObjectRequest(r -> r.bucket(bucket).key(key))
                     .signatureDuration(Duration.between(Instant.now(), expiration.toInstant())))
                 .url();
diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3Streaming.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3Streaming.java
index 9bab4fc56775..3b3f878e3773 100644
--- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3Streaming.java
+++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/S3Streaming.java
@@ -18,8 +18,10 @@
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import software.amazon.awssdk.core.ResponseInputStream;
 import software.amazon.awssdk.core.sync.RequestBody;
@@ -30,6 +32,8 @@
 import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
 import software.amazon.awssdk.services.s3.model.PutObjectRequest;
 import software.amazon.awssdk.services.s3.model.RequestPayer;
+import software.amazon.awssdk.services.s3.model.Tag;
+import software.amazon.awssdk.services.s3.model.Tagging;
 
 public class S3Streaming {
 
@@ -61,6 +65,8 @@ void putObject_bucketKeyStreamMetadata(String bucket, String key, InputStream st
         HeadObjectResponse metadataWithoutLength = HeadObjectResponse.builder()
             .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with S3Client, Content-Length should be specified and used with RequestBody.fromInputStream(). Otherwise, the entire stream will be buffered in memory. If content length must be unknown, we recommend using the CRT-based S3 client - https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/crt-based-s3-client.html*/s3.putObject(PutObjectRequest.builder().bucket(bucket).key(key).build(), RequestBody.fromContentProvider(() -> stream, "application/octet-stream"));
+
+        /*AWS SDK for Java v2 migration: When using InputStream to upload with S3Client, Content-Length should be specified and used with RequestBody.fromInputStream(). Otherwise, the entire stream will be buffered in memory. If content length must be unknown, we recommend using the CRT-based S3 client - https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/crt-based-s3-client.html*/s3.putObject(PutObjectRequest.builder().bucket("bucket").key("key").build(), RequestBody.fromContentProvider(() -> stream, "application/octet-stream"));
     }
 
     /**
@@ -108,10 +114,16 @@ void putObject_requestPojoWithoutPayload(String bucket, String key) {
 
 
     void putObjectSetters() {
+        List<Tag> tags = new ArrayList<>();
+        Tagging objectTagging = Tagging.builder().tagSet(tags)
+            .build();
+
         PutObjectRequest putObjectRequest =
             PutObjectRequest.builder().bucket("bucket").key("key").websiteRedirectLocation("location")
                 .bucket("bucketName")
+                .websiteRedirectLocation("redirectLocation")
                 .acl(ObjectCannedACL.AWS_EXEC_READ)
+                .tagging(objectTagging)
             .build();
     }
 
@@ -160,7 +172,7 @@ void putObjectRequest_withMetadata() {
             .build();
     }
 
-    void putObjectRequester_emptyMetadata() {
+    void putObjectRequest_emptyMetadata() {
         HeadObjectResponse emptyMetadata1 = HeadObjectResponse.builder()
             .build();
         PutObjectRequest request1 =PutObjectRequest.builder().bucket("bucket").key("key").websiteRedirectLocation("location")
diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/S3Streaming.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/S3Streaming.java
index ab8243333b3a..e974de1e21b5 100644
--- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/S3Streaming.java
+++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/S3Streaming.java
@@ -19,13 +19,17 @@
 import com.amazonaws.services.s3.AmazonS3ClientBuilder;
 import com.amazonaws.services.s3.model.CannedAccessControlList;
 import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.amazonaws.services.s3.model.ObjectTagging;
 import com.amazonaws.services.s3.model.PutObjectRequest;
 import com.amazonaws.services.s3.model.S3Object;
+import com.amazonaws.services.s3.model.Tag;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class S3Streaming {
@@ -53,6 +57,8 @@ void putObject_bucketKeyStreamMetadata(String bucket, String key, InputStream st
 
         ObjectMetadata metadataWithoutLength = new ObjectMetadata();
         s3.putObject(bucket, key, stream, metadataWithoutLength);
+
+        s3.putObject("bucket", "key", stream, new ObjectMetadata());
     }
 
     /**
@@ -95,10 +101,15 @@ void putObject_requestPojoWithoutPayload(String bucket, String key) {
 
 
     void putObjectSetters() {
+        List<Tag> tags = new ArrayList<>();
+        ObjectTagging objectTagging = new ObjectTagging(tags);
+
         PutObjectRequest putObjectRequest =
             new PutObjectRequest("bucket", "key", "location")
-            .withBucketName("bucketName")
-                .withCannedAcl(CannedAccessControlList.AwsExecRead);
+                .withBucketName("bucketName")
+                .withRedirectLocation("redirectLocation")
+                .withCannedAcl(CannedAccessControlList.AwsExecRead)
+                .withTagging(objectTagging);
     }
 
     void putObjectRequesterPaysSetter() {
@@ -141,7 +152,7 @@ void putObjectRequest_withMetadata() {
         PutObjectRequest request = new PutObjectRequest("bucket", "key", "location").withMetadata(metadata);
     }
 
-    void putObjectRequester_emptyMetadata() {
+    void putObjectRequest_emptyMetadata() {
         ObjectMetadata emptyMetadata1 = new ObjectMetadata();
         PutObjectRequest request1 = new PutObjectRequest("bucket", "key", "location").withMetadata(emptyMetadata1);
 
diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java
index 94fb449296ad..151d24a95c24 100644
--- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java
+++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java
@@ -18,16 +18,19 @@
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.SUPPORTED_METADATA_TRANSFORMS;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V2_S3_MODEL_PKG;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V2_TM_MODEL_PKG;
+import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.addCommentForUnsupportedPutObjectRequestSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.addMetadataFields;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.createComments;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.getArgumentName;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.getSelectName;
+import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.inputStreamBufferingWarningComment;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isObjectMetadataSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isPayloadSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isPutObjectRequestBuilderSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isPutObjectRequestSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isRequestMetadataSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isRequestPayerSetter;
+import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isUnsupportedPutObjectRequestSetter;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.v2S3MethodMatcher;
 import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.v2TmMethodMatcher;
 import static software.amazon.awssdk.v2migration.internal.utils.SdkTypeUtils.isFileType;
@@ -36,7 +39,6 @@
 import java.util.ArrayDeque;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 import org.openrewrite.ExecutionContext;
@@ -46,7 +48,6 @@
 import org.openrewrite.java.JavaTemplate;
 import org.openrewrite.java.JavaVisitor;
 import org.openrewrite.java.MethodMatcher;
-import org.openrewrite.java.tree.Comment;
 import org.openrewrite.java.tree.Expression;
 import org.openrewrite.java.tree.J;
 import software.amazon.awssdk.annotations.SdkInternalApi;
@@ -106,6 +107,10 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx)
                 if (isRequestPayerSetter(method)) {
                     return transformWithRequesterPays(method);
                 }
+                if (isUnsupportedPutObjectRequestSetter(method)) {
+                    method = addCommentForUnsupportedPutObjectRequestSetter(method);
+                    return super.visitMethodInvocation(method, ctx);
+                }
             }
             if (isPutObjectRequestSetter(method)) {
                 if (isRequestMetadataSetter(method)) {
@@ -316,11 +321,18 @@ private J.MethodInvocation transformPutObjectWithStreamAndMetadata(J.MethodInvoc
             addS3Import("PutObjectRequest");
 
             Expression metadata = method.getArguments().get(3);
-            String metadataName = ((J.Identifier) metadata).getSimpleName();
+            String metadataName = null;
+            if (metadata instanceof J.Identifier) {
+                metadataName = ((J.Identifier) metadata).getSimpleName();
+            }
 
             StringBuilder sb = new StringBuilder("PutObjectRequest.builder().bucket(#{any()}).key(#{any()})");
-            addMetadataFields(sb, metadataName, metadataMap);
-            Expression contentLen = retrieveContentLengthForMetadataIfSet(metadataName);
+
+            Expression contentLen = null;
+            if (metadataName != null) {
+                addMetadataFields(sb, metadataName, metadataMap);
+                contentLen = retrieveContentLengthForMetadataIfSet(metadataName);
+            }
 
             Expression[] params = {method.getArguments().get(0), method.getArguments().get(1),
                                    method.getArguments().get(2)};
@@ -473,13 +485,5 @@ private void addTmImport(String pojoName) {
             String fqcn = V2_TM_MODEL_PKG + pojoName;
             doAfterVisit(new AddImport<>(fqcn, null, false));
         }
-
-        private List<Comment> inputStreamBufferingWarningComment() {
-            String warning = "When using InputStream to upload with S3Client, Content-Length should be specified and used "
-                             + "with RequestBody.fromInputStream(). Otherwise, the entire stream will be buffered in memory. If"
-                             + " content length must be unknown, we recommend using the CRT-based S3 client - "
-                             + "https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/crt-based-s3-client.html";
-            return createComments(warning);
-        }
     }
 }
diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/NamingConversionUtils.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/NamingConversionUtils.java
index 5cecb0a5b779..50edbb9e6716 100644
--- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/NamingConversionUtils.java
+++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/NamingConversionUtils.java
@@ -180,6 +180,8 @@ public final class NamingConversionUtils {
         S3_POJO_MAPPING.put("ReplicationFilter", "ReplicationRuleFilter");
         S3_POJO_MAPPING.put("ReplicationAndOperator", "ReplicationRuleAndOperator");
         S3_POJO_MAPPING.put("PartETag", "CompletedPart");
+        S3_POJO_MAPPING.put("ObjectTagging", "Tagging");
+        S3_POJO_MAPPING.put("AccessControlList", "AccessControlPolicy");
 
         //Special Enum mapping
         S3_POJO_MAPPING.put("S3Event", "Event");
diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java
index 386cf7aa575c..adad6c5d8fc0 100644
--- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java
+++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java
@@ -61,6 +61,13 @@ public final class S3TransformUtils {
         "httpExpiresDate"
     )));
 
+    public static final Set<String> UNSUPPORTED_PUT_OBJ_REQUEST_TRANSFORMS = Collections.unmodifiableSet(new HashSet<>(
+        Arrays.asList(
+            "sseCustomerKey",
+            "sseAwsKeyManagementParams",
+            "accessControlList"
+        )));
+
 
     private S3TransformUtils() {
 
@@ -153,9 +160,29 @@ public static String getSelectName(J.MethodInvocation method) {
         return ((J.Identifier) select).getSimpleName();
     }
 
+    private static Comment generateComment(String comment, boolean withNewLine) {
+        String suffix = withNewLine ? "\n" : "";
+        return new TextComment(true, "AWS SDK for Java v2 migration: " + comment, suffix, Markers.EMPTY);
+    }
+
+    public static Comment createComment(String comment) {
+        return generateComment(comment, false);
+    }
+
+    public static Comment createCommentWithNewline(String comment) {
+        return generateComment(comment, true);
+    }
+
     public static List<Comment> createComments(String comment) {
-        return Collections.singletonList(
-            new TextComment(true, "AWS SDK for Java v2 migration: " + comment, "", Markers.EMPTY));
+        return Arrays.asList(createComment(comment));
+    }
+
+    public static List<Comment> createCommentsWithNewline(String comment) {
+        return Arrays.asList(createCommentWithNewline(comment));
+    }
+
+    public static boolean isUnsupportedPutObjectRequestSetter(J.MethodInvocation method) {
+        return UNSUPPORTED_PUT_OBJ_REQUEST_TRANSFORMS.contains(method.getSimpleName());
     }
 
     public static boolean isObjectMetadataSetter(J.MethodInvocation method) {
@@ -221,24 +248,34 @@ public static boolean isUnsupportedHttpMethod(String httpMethod) {
         return Arrays.asList("Head", "Post", "Patch").contains(httpMethod);
     }
 
+    public static List<Comment> inputStreamBufferingWarningComment() {
+        String warning = "When using InputStream to upload with S3Client, Content-Length should be specified and used "
+                         + "with RequestBody.fromInputStream(). Otherwise, the entire stream will be buffered in memory. If"
+                         + " content length must be unknown, we recommend using the CRT-based S3 client - "
+                         + "https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/crt-based-s3-client.html";
+        return createComments(warning);
+    }
+
     public static List<Comment> assignedVariableHttpMethodNotSupportedComment() {
         String comment = "Transform for S3 generatePresignedUrl() with an assigned variable for HttpMethod is not supported."
-                         + " Please manually migrate your code - https://sdk.amazonaws"
-                         + ".com/java/api/latest/software/amazon/awssdk/services/s3/presigner/S3Presigner.html";
+                         + " Please manually migrate your code - "
+                         + "https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/presigner/S3Presigner"
+                         + ".html";
         return createComments(comment);
     }
 
     public static List<Comment> requestPojoTransformNotSupportedComment() {
-        String comment = "Transforms are not supported for GeneratePresignedUrlRequest, please manually migrate your code "
-                         + "- https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/presigner"
-                         + "/S3Presigner.html";
+        String comment = "Transforms are not supported for GeneratePresignedUrlRequest, please manually migrate your code - "
+                         + "https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/presigner/S3Presigner"
+                         + ".html";
         return createComments(comment);
     }
 
     public static List<Comment> httpMethodNotSupportedComment(String httpMethod) {
         String comment = String.format("S3 generatePresignedUrl() with %s HTTP method is not supported in v2. Only GET, PUT, "
-                                       + "and DELETE are supported - https://sdk.amazonaws"
-                                       + ".com/java/api/latest/software/amazon/awssdk/services/s3/presigner/S3Presigner.html",
+                                       + "and DELETE are supported - "
+                                       + "https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3"
+                                       + "/presigner/S3Presigner.html",
                                        httpMethod.toUpperCase(Locale.ROOT));
         return createComments(comment);
     }
@@ -247,6 +284,54 @@ public static List<Comment> presignerSingleInstanceSuggestion() {
         String comment = "If generating multiple pre-signed URLs, it is recommended to create a single instance of "
                          + "S3Presigner, since creating a presigner can be expensive. If applicable, please manually "
                          + "refactor the transformed code.";
-        return createComments(comment);
+        return createCommentsWithNewline(comment);
+    }
+
+    public static J.MethodInvocation sseAwsKeyManagementParamsNotSupportedComment(J.MethodInvocation method) {
+        String comment = "Transform for PutObjectRequest setter sseAwsKeyManagementParam is not supported, please manually "
+                         + "migrate your code to use the v2 setters: ssekmsKeyId, serverSideEncryption, sseCustomerAlgorithm - "
+                         + "https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model"
+                         + "/PutObjectRequest.Builder.html#ssekmsKeyId(java.lang.String)";
+        return appendCommentToMethod(method, comment);
+    }
+
+    public static J.MethodInvocation sseCustomerKeyNotSupportedComment(J.MethodInvocation method) {
+        String comment = "Transform for PutObjectRequest setter sseCustomerKey is not supported, please manually "
+                         + "migrate your code to use the v2 setters: sseCustomerKey, sseCustomerKeyMD5 - "
+                         + "https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model"
+                         + "/PutObjectRequest.Builder.html#sseCustomerKey(java.lang.String)";
+        return appendCommentToMethod(method, comment);
+    }
+
+    public static J.MethodInvocation accessControlListNotSupportedComment(J.MethodInvocation method) {
+        String comment = "Transform for PutObjectRequest setter accessControlList is not supported, please manually "
+                         + "migrate your code to use the v2 setters: acl, grantReadACP, grantWriteACP - "
+                         + "https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model"
+                         + "/PutObjectRequest.Builder.html#acl(java.lang.String)";
+        return appendCommentToMethod(method, comment);
+    }
+
+    public static J.MethodInvocation addCommentForUnsupportedPutObjectRequestSetter(J.MethodInvocation method) {
+        String methodName = method.getSimpleName();
+        switch (methodName) {
+            case "sseCustomerKey":
+                return sseAwsKeyManagementParamsNotSupportedComment(method);
+            case "sseAwsKeyManagementParams":
+                return sseCustomerKeyNotSupportedComment(method);
+            case "accessControlList":
+                return accessControlListNotSupportedComment(method);
+            default:
+                return method;
+        }
+    }
+
+    public static J.MethodInvocation appendCommentToMethod(J.MethodInvocation method, String comment) {
+        if (method.getComments().isEmpty()) {
+            return method.withComments(createCommentsWithNewline(comment));
+        }
+
+        List<Comment> existingComments = method.getComments();
+        existingComments.add(createCommentWithNewline(comment));
+        return method.withComments(existingComments);
     }
 }
diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/SdkTypeUtils.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/SdkTypeUtils.java
index 85a4b494f941..667370ef1ed0 100644
--- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/SdkTypeUtils.java
+++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/SdkTypeUtils.java
@@ -148,7 +148,10 @@ public final class SdkTypeUtils {
                       "com.amazonaws.services.kinesis.metrics",
                       "com.amazonaws.services.kinesis.multilang",
                       // Kinesis Producer Library (KCL) : amazon-kinesis-producer
-                      "com.amazonaws.services.kinesis.producer"
+                      "com.amazonaws.services.kinesis.producer",
+                      // S3 POJOs with no v2 equivalent
+                      "com.amazonaws.services.s3.model.SSEAwsKeyManagementParams",
+                      "com.amazonaws.services.s3.model.SSECustomerKey"
         ));
 
     static {
diff --git a/v2-migration/src/main/resources/META-INF/rewrite/change-s3-methods.yml b/v2-migration/src/main/resources/META-INF/rewrite/change-s3-methods.yml
index 2034463d8b93..ad1faf18180c 100644
--- a/v2-migration/src/main/resources/META-INF/rewrite/change-s3-methods.yml
+++ b/v2-migration/src/main/resources/META-INF/rewrite/change-s3-methods.yml
@@ -196,12 +196,6 @@ recipeList:
   - org.openrewrite.java.ChangeMethodName:
       methodPattern: com.amazonaws.services.s3.model.BucketCrossOriginConfiguration withRules(com.amazonaws.services.s3.model.CORSRule...)
       newMethodName: withCorsRules
-  - org.openrewrite.java.ChangeMethodName:
-      methodPattern: com.amazonaws.services.s3.model.ListObjectsRequest withBucketName(String)
-      newMethodName: withBucket
-  - org.openrewrite.java.ChangeMethodName:
-      methodPattern: com.amazonaws.services.s3.model.ListObjectsV2Request withBucketName(String)
-      newMethodName: withBucket
   - org.openrewrite.java.ChangeMethodName:
       methodPattern: com.amazonaws.services.s3.model.CopyPartRequest withSourceBucketName(String)
       newMethodName: withSourceBucket
@@ -211,17 +205,13 @@ recipeList:
   - org.openrewrite.java.ChangeMethodName:
       methodPattern: com.amazonaws.services.s3.model.analytics.AnalyticsS3BucketDestination withBucketArn(String)
       newMethodName: withBucket
-  - org.openrewrite.java.ChangeMethodName:
-      methodPattern: com.amazonaws.services.s3.model.CompleteMultipartUploadRequest withBucketName(String)
-      newMethodName: withBucket
   - org.openrewrite.java.ChangeMethodName:
       methodPattern: com.amazonaws.services.s3.model.CompleteMultipartUploadRequest withPartETags(java.util.List)
       newMethodName: withMultipartUpload
 
-
   - org.openrewrite.java.ChangeMethodName:
-      methodPattern: com.amazonaws.services.s3.model.PutObjectRequest withBucketName(String)
-      newMethodName: withBucket
+      methodPattern: com.amazonaws.services.s3.model.PutObjectRequest withRedirectLocation(String)
+      newMethodName: withWebsiteRedirectLocation
   - org.openrewrite.java.ChangeMethodName:
       methodPattern: com.amazonaws.services.s3.model.PutObjectRequest withCannedAcl(com.amazonaws.services.s3.model.CannedAccessControlList)
       newMethodName: withAcl
diff --git a/v2-migration/src/main/resources/META-INF/rewrite/s3-methods-constructor-to-fluent.yml b/v2-migration/src/main/resources/META-INF/rewrite/s3-methods-constructor-to-fluent.yml
index 8510bd53e269..0d5593d8e1c1 100644
--- a/v2-migration/src/main/resources/META-INF/rewrite/s3-methods-constructor-to-fluent.yml
+++ b/v2-migration/src/main/resources/META-INF/rewrite/s3-methods-constructor-to-fluent.yml
@@ -621,4 +621,10 @@ recipeList:
         - withBucket
         - withKey
         - withUploadId
-        - withMultipartUpload
\ No newline at end of file
+        - withMultipartUpload
+  - software.amazon.awssdk.v2migration.ConstructorToFluent:
+      clzzFqcn: com.amazonaws.services.s3.model.ObjectTagging
+      parameterTypes:
+        - java.util.List<com.amazonaws.services.s3.model.Tag>
+      fluentNames:
+        - withTagSet
\ No newline at end of file


Reviews:
user: login: davidh44
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T17:56:22Z
user: login: L-Applin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T19:27:00Z
user: login: davidh44
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T20:10:19Z
user: login: L-Applin
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T15:28:32Z

Review comments:
diff_hunk: @@ -196,12 +196,6 @@ recipeList:
   - org.openrewrite.java.ChangeMethodName:
       methodPattern: com.amazonaws.services.s3.model.BucketCrossOriginConfiguration withRules(com.amazonaws.services.s3.model.CORSRule...)
       newMethodName: withCorsRules
-  - org.openrewrite.java.ChangeMethodName:
-      methodPattern: com.amazonaws.services.s3.model.ListObjectsRequest withBucketName(String)
-      newMethodName: withBucket
-  - org.openrewrite.java.ChangeMethodName:
-      methodPattern: com.amazonaws.services.s3.model.ListObjectsV2Request withBucketName(String)
-      newMethodName: withBucket
path: v2-migration/src/main/resources/META-INF/rewrite/change-s3-methods.yml
user: login: davidh44
body: Removing these as they are redundant with recent recipe addition to remove in V1SetterToV2

https://github.com/aws/aws-sdk-java-v2/blob/master/v2-migration/src/main/java/software/amazon/awssdk/v2migration/V1SetterToV2.java#L112
created_at: 2025-04-01T17:56:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,33 +35,49 @@ public class S3Transforms {
 
     void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String key) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndLiteralLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location").contentLength(333L)
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndLiteralLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, 333, newExecutorServiceVariableToDefine)).build());
     }
 
     void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String key) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
         long contentLen = 777;
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndAssignedLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location").contentLength(contentLen)
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndAssignedLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, contentLen, newExecutorServiceVariableToDefine)).build());
     }
 
     void upload_streamWithoutLength(S3TransferManager tm, String bucket, String key) {
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndNoLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location")
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndNoLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, -1L, newExecutorServiceVariableToDefine)).build());
     }
 
+    void putObjectRequest_unsupportedSetters() {
+        SSECustomerKey sseCustomerKey = new SSECustomerKey("val");
+        SSEAwsKeyManagementParams sseParams = new SSEAwsKeyManagementParams();
+        AccessControlPolicy accessControlList = AccessControlPolicy.builder()
+            .build();
+
+        PutObjectRequest request = /*AWS SDK for Java v2 migration: Transform for PutObjectRequest setter accessControlList is not supported, please manually migrate your code to use the v2 setters: acl, grantReadACP, grantWriteACP - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/PutObjectRequest.Builder.html#acl(java.lang.String)*/
path: test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java
user: login: L-Applin
body: Is there an easy way to combine all comments for a method invocation into the same comments?
created_at: 2025-04-01T19:26:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,33 +35,49 @@ public class S3Transforms {
 
     void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String key) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndLiteralLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location").contentLength(333L)
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndLiteralLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, 333, newExecutorServiceVariableToDefine)).build());
     }
 
     void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String key) {
         HeadObjectResponse metadata = HeadObjectResponse.builder()
-                .build();
+            .build();
         long contentLen = 777;
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndAssignedLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location").contentLength(contentLen)
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndAssignedLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, contentLen, newExecutorServiceVariableToDefine)).build());
     }
 
     void upload_streamWithoutLength(S3TransferManager tm, String bucket, String key) {
         InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
         PutObjectRequest requestWithStreamAndNoLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location")
-                .build();
+            .build();
         /*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndNoLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, -1L, newExecutorServiceVariableToDefine)).build());
     }
 
+    void putObjectRequest_unsupportedSetters() {
+        SSECustomerKey sseCustomerKey = new SSECustomerKey("val");
+        SSEAwsKeyManagementParams sseParams = new SSEAwsKeyManagementParams();
+        AccessControlPolicy accessControlList = AccessControlPolicy.builder()
+            .build();
+
+        PutObjectRequest request = /*AWS SDK for Java v2 migration: Transform for PutObjectRequest setter accessControlList is not supported, please manually migrate your code to use the v2 setters: acl, grantReadACP, grantWriteACP - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/PutObjectRequest.Builder.html#acl(java.lang.String)*/
path: test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java
user: login: davidh44
body: Not that I can think of. We'd have to retrieve and parse the comment. Seems better to put on separate lines especially with the links to javadocs.
created_at: 2025-04-01T20:10:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-04-01T20:18:17Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=aws_aws-sdk-java-v2&pullRequest=6001) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aws_aws-sdk-java-v2&pullRequest=6001&metric=new_coverage&view=list) (required ≥ 80%)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aws_aws-sdk-java-v2&pullRequest=6001)


author_association: NONE