Upgrade dependencies and migrate to latest AWS SDK API

Update dependencies
- AWS Java SDK to 2.35.7
- Docker Java API to 3.6.0
- dynamodb-lock-client to 1.4.0
- jackson to 2.20.0
- jna to 5.18.1
- joda-time to 2.14.0
- testcontainers to 1.21.3

Migrate to the current AWS Java SDK API and remove
dependencies of the old one.

Change-Id: Ib5bae5e3fbe4f8fd552ada864350500a4079f445
diff --git a/BUILD b/BUILD
index f45d775..279e50b 100644
--- a/BUILD
+++ b/BUILD
@@ -18,19 +18,34 @@
     resources = glob(["src/main/resources/**/*"]),
     deps = [
         ":global-refdb-neverlink",
-        "@amazon-aws-core//jar",
-        "@amazon-dynamodb//jar",
-        "@amazon-regions//jar",
+        "@amazon-dynamodb-lock-client//jar",
+        "@amazon-sdk-apache-client//jar",
+        "@amazon-sdk-auth//jar",
+        "@amazon-sdk-aws-core//jar",
+        "@amazon-sdk-aws-json-protocol//jar",
+        "@amazon-sdk-checksums-spi//jar",
+        "@amazon-sdk-checksums//jar",
         "@amazon-sdk-core//jar",
-        "@amazon-utils//jar",
-        "@aws-java-sdk-core//jar",
-        "@aws-java-sdk-dynamodb//jar",
-        "@dynamodb-lock-client//jar",
+        "@amazon-sdk-dynamodb//jar",
+        "@amazon-sdk-endpoints-spi//jar",
+        "@amazon-sdk-http-auth-aws//jar",
+        "@amazon-sdk-http-auth-spi//jar",
+        "@amazon-sdk-http-auth//jar",
+        "@amazon-sdk-http-client-spi//jar",
+        "@amazon-sdk-identity-spi//jar",
+        "@amazon-sdk-metrics-spi//jar",
+        "@amazon-sdk-profiles//jar",
+        "@amazon-sdk-regions//jar",
+        "@amazon-sdk-retries-spi//jar",
+        "@amazon-sdk-retries//jar",
+        "@amazon-sdk-third-party-jackson-core//jar",
+        "@amazon-sdk-utils//jar",
         "@jackson-annotations//jar",
         "@jackson-core//jar",
         "@jackson-databind//jar",
         "@jackson-dataformat-cbor//jar",
         "@joda-time//jar",
+        "@reactive-streams//jar",
     ],
 )
 
@@ -39,7 +54,9 @@
     timeout = "long",
     srcs = glob(["src/test/java/**/*.java"]),
     resources = glob(["src/test/resources/**/*"]),
-    tags = ["aws-dynamodb-refdb"],
+    tags = [
+        "aws-dynamodb-refdb",
+    ],
     deps = [
         ":aws-dynamodb-refdb__plugin_test_deps",
     ],
@@ -52,9 +69,16 @@
     exports = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
         ":aws-dynamodb-refdb__plugin",
         "//plugins/global-refdb",
-        "@amazon-regions//jar",
-        "@aws-java-sdk-dynamodb//jar",
+        "@amazon-sdk-auth//jar",
+        "@amazon-sdk-dynamodb//jar",
+        "@amazon-sdk-http-client-spi//jar",
+        "@amazon-sdk-identity-spi//jar",
+        "@amazon-sdk-json-utils//jar",
+        "@amazon-sdk-netty-nio-client//jar",
+        "@amazon-sdk-protocol-core//jar",
+        "@amazon-sdk-regions//jar",
         "@docker-java-api//jar",
+        "@docker-java-transport-zerodep//jar",
         "@docker-java-transport//jar",
         "@duct-tape//jar",
         "@jna//jar",
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 8267357..d0046e7 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -1,99 +1,211 @@
 load("//tools/bzl:maven_jar.bzl", "maven_jar")
 
-AWS_SDK_VER = "2.16.19"
-AWS_KINESIS_VER = "2.3.4"
-JACKSON_VER = "2.10.4"
-DOCKER_JAVA_VERS = "3.2.8"
-
 def external_plugin_deps():
+    """Dependencies of the aws-dynamodb-refdb plugin"""
+
+    AWS_SDK_VER = "2.35.7"
+
     maven_jar(
-        name = "amazon-dynamodb",
-        artifact = "software.amazon.awssdk:dynamodb:" + AWS_SDK_VER,
-        sha1 = "33ec7d291973658779b5777db2a0214a5c469e81",
+        name = "amazon-dynamodb-lock-client",
+        artifact = "com.amazonaws:dynamodb-lock-client:1.4.0",
+        sha1 = "6aae4bc1b4eeab582e370333b2174623c62f82c7",
     )
 
     maven_jar(
-        name = "aws-java-sdk-dynamodb",
-        artifact = "com.amazonaws:aws-java-sdk-dynamodb:1.11.1006",
-        sha1 = "dd2c9dff101ae8dad26197f7b09a06d4e13965ca",
+        name = "amazon-sdk-apache-client",
+        artifact = "software.amazon.awssdk:apache-client:" + AWS_SDK_VER,
+        sha1 = "e3ba5ade5c38238a8735bd07d030afb1286d6a0d",
     )
 
     maven_jar(
-        name = "dynamodb-lock-client",
-        artifact = "com.amazonaws:dynamodb-lock-client:1.1.0",
-        sha1 = "3aadced3599f3b2fd058bc75d48dde374f66e544",
+        name = "amazon-sdk-auth",
+        artifact = "software.amazon.awssdk:auth:" + AWS_SDK_VER,
+        sha1 = "371377f6eeb54cc0d847536208bdf4a4685ea48c",
     )
 
     maven_jar(
-        name = "amazon-regions",
-        artifact = "software.amazon.awssdk:regions:" + AWS_SDK_VER,
-        sha1 = "089f4f3d3ef20b2486f09e71da638c03100eab64",
+        name = "amazon-sdk-aws-core",
+        artifact = "software.amazon.awssdk:aws-core:" + AWS_SDK_VER,
+        sha1 = "a139bfdf6125379b3153d12b66b33597a60f1f8e",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-aws-json-protocol",
+        artifact = "software.amazon.awssdk:aws-json-protocol:" + AWS_SDK_VER,
+        sha1 = "fe3580b0839932b5922fe16b68b6a0937131aba7",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-checksums",
+        artifact = "software.amazon.awssdk:checksums:" + AWS_SDK_VER,
+        sha1 = "47ad6340a76b91706dff61a65e4318a791754733",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-checksums-spi",
+        artifact = "software.amazon.awssdk:checksums-spi:" + AWS_SDK_VER,
+        sha1 = "a924e1f21f41be1fd37563938c4d5ff3287c54ba",
     )
 
     maven_jar(
         name = "amazon-sdk-core",
         artifact = "software.amazon.awssdk:sdk-core:" + AWS_SDK_VER,
-        sha1 = "02a60fd9c138048272ef8b6c80ae67491dd386a9",
+        sha1 = "8e4fd7f3020a00ff00e8ca2dacbd2256486dbf93",
     )
 
     maven_jar(
-        name = "amazon-aws-core",
-        artifact = "software.amazon.awssdk:aws-core:" + AWS_SDK_VER,
-        sha1 = "0f50f5cf2698a0de7d2d77322cbf3fb13f76187f",
+        name = "amazon-sdk-dynamodb",
+        artifact = "software.amazon.awssdk:dynamodb:" + AWS_SDK_VER,
+        sha1 = "40ef2f0e7256a03a97f5fa65b140f947538e8b7e",
     )
 
     maven_jar(
-        name = "amazon-utils",
+        name = "amazon-sdk-endpoints-spi",
+        artifact = "software.amazon.awssdk:endpoints-spi:" + AWS_SDK_VER,
+        sha1 = "99f906164192dc3242aad2e17313c48231b7fb0b",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-http-auth",
+        artifact = "software.amazon.awssdk:http-auth:" + AWS_SDK_VER,
+        sha1 = "0922970e363017e3eba324e7cd2d3a92b87fc97f",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-http-auth-aws",
+        artifact = "software.amazon.awssdk:http-auth-aws:" + AWS_SDK_VER,
+        sha1 = "cef979ee5f39359b75189363867800cee6ff8fc1",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-http-auth-spi",
+        artifact = "software.amazon.awssdk:http-auth-spi:" + AWS_SDK_VER,
+        sha1 = "f547073978d6f5968b08bc5a1eae1c689dd50006",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-http-client-spi",
+        artifact = "software.amazon.awssdk:http-client-spi:" + AWS_SDK_VER,
+        sha1 = "55aaa5e6b03302b3d801f16e5cfb1b71de983bff",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-identity-spi",
+        artifact = "software.amazon.awssdk:identity-spi:" + AWS_SDK_VER,
+        sha1 = "d3cc80a7a6a99492b242482c25020c737cbf3959",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-json-utils",
+        artifact = "software.amazon.awssdk:json-utils:" + AWS_SDK_VER,
+        sha1 = "013636801d30c51e0e18f12636a75db8f45f501b",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-metrics-spi",
+        artifact = "software.amazon.awssdk:metrics-spi:" + AWS_SDK_VER,
+        sha1 = "320c8205baad6a6e15d868c7f45b74f497b59d3e",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-netty-nio-client",
+        artifact = "software.amazon.awssdk:netty-nio-client:" + AWS_SDK_VER,
+        sha1 = "98e60e5af0f421b42b9afc582d3f08ce7fc9c58f",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-profiles",
+        artifact = "software.amazon.awssdk:profiles:" + AWS_SDK_VER,
+        sha1 = "199c71ed266e97011dd999e2a41d2d305dd8baa9",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-protocol-core",
+        artifact = "software.amazon.awssdk:protocol-core:" + AWS_SDK_VER,
+        sha1 = "e02285068d1b7c78e09d0a7233f94c07c9d0c5bb",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-regions",
+        artifact = "software.amazon.awssdk:regions:" + AWS_SDK_VER,
+        sha1 = "43a383d970fbaabb61ba261192c9a592f94b491d",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-retries",
+        artifact = "software.amazon.awssdk:retries:" + AWS_SDK_VER,
+        sha1 = "81e625923a487d4a24e3e546ec74620b8ccc9e92",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-retries-spi",
+        artifact = "software.amazon.awssdk:retries-spi:" + AWS_SDK_VER,
+        sha1 = "a902e42e49b32799af4b76f176cae715298dcdd8",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-third-party-jackson-core",
+        artifact = "software.amazon.awssdk:third-party-jackson-core:" + AWS_SDK_VER,
+        sha1 = "039256c93502cb9a0d859600ce4d0623c45bdc55",
+    )
+
+    maven_jar(
+        name = "amazon-sdk-utils",
         artifact = "software.amazon.awssdk:utils:" + AWS_SDK_VER,
-        sha1 = "53edaa1f884682ac3091293eff3eb024ed0e36bb",
+        sha1 = "1240513bdae17b33857d34af95c7f8bd3090e2d0",
     )
 
     maven_jar(
-        name = "aws-java-sdk-core",
-        artifact = "com.amazonaws:aws-java-sdk-core:1.11.960",
-        sha1 = "18b6b2a5cb83a0e2e33a593302b5dbe0ca2ade64",
+        name = "reactive-streams",
+        artifact = "org.reactivestreams:reactive-streams:1.0.4",
+        sha1 = "3864a1320d97d7b045f729a326e1e077661f31b7",
     )
 
+    JACKSON_VER = "2.20.0"
+
     maven_jar(
         name = "jackson-databind",
         artifact = "com.fasterxml.jackson.core:jackson-databind:" + JACKSON_VER,
-        sha1 = "76e9152e93d4cf052f93a64596f633ba5b1c8ed9",
+        sha1 = "f0a5e62fbd21285e9a5498a60dccb097e1ef793b",
     )
 
     maven_jar(
         name = "jackson-dataformat-cbor",
         artifact = "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:" + JACKSON_VER,
-        sha1 = "c854bb2d46138198cb5d4aae86ef6c04b8bc1e70",
+        sha1 = "c10e9032bec62df3089ca1cbdef43a1453aca261",
     )
 
     maven_jar(
         name = "jackson-annotations",
-        artifact = "com.fasterxml.jackson.core:jackson-annotations:" + JACKSON_VER,
-        sha1 = "6ae6028aff033f194c9710ad87c224ccaadeed6c",
+        # the third part of the version number was missed in this release
+        artifact = "com.fasterxml.jackson.core:jackson-annotations:2.20",
+        sha1 = "6a5e7291ea3f2b590a7ce400adb7b3aea4d7e12c",
     )
 
     maven_jar(
         name = "jackson-core",
         artifact = "com.fasterxml.jackson.core:jackson-core:" + JACKSON_VER,
-        sha1 = "8796585e716440d6dd5128b30359932a9eb74d0d",
+        sha1 = "3c97f7fad069f7cfae639d790bd93d6a0b2dff31",
     )
 
     maven_jar(
         name = "joda-time",
-        artifact = "joda-time:joda-time:2.10.10",
-        sha1 = "29e8126e31f41e5c12b9fe3a7eb02e704c47d70b",
+        artifact = "joda-time:joda-time:2.14.0",
+        sha1 = "1fa665c1ce64a2c8c94f63fc5c1ee7bd742d2022",
     )
 
+    TESTCONTAINERS_VERS = "1.21.3"
+
     maven_jar(
         name = "testcontainer-localstack",
-        artifact = "org.testcontainers:localstack:1.15.2",
-        sha1 = "ae3c4717bc5f37410abbb490cb46d349a77990a0",
+        artifact = "org.testcontainers:localstack:" + TESTCONTAINERS_VERS,
+        sha1 = "86cd23aaba16741005c794d26419a16c8470a8e1",
     )
 
     maven_jar(
         name = "testcontainers",
-        artifact = "org.testcontainers:testcontainers:1.15.3",
-        sha1 = "95c6cfde71c2209f0c29cb14e432471e0b111880",
+        artifact = "org.testcontainers:testcontainers:" + TESTCONTAINERS_VERS,
+        sha1 = "aa3e792d2cf4598019933c42f1cfa55bd608ce8b",
     )
 
     maven_jar(
@@ -102,16 +214,24 @@
         sha1 = "20d31a578030ec8e941888537267d3123c2ad1c1",
     )
 
+    DOCKER_JAVA_VERS = "3.6.0"
+
     maven_jar(
         name = "docker-java-api",
         artifact = "com.github.docker-java:docker-java-api:" + DOCKER_JAVA_VERS,
-        sha1 = "4ac22a72d546a9f3523cd4b5fabffa77c4a6ec7c",
+        sha1 = "caeb5bee6a9c07bff31f73ace576436168e2aa47",
     )
 
     maven_jar(
         name = "docker-java-transport",
         artifact = "com.github.docker-java:docker-java-transport:" + DOCKER_JAVA_VERS,
-        sha1 = "c3b5598c67d0a5e2e780bf48f520da26b9915eab",
+        sha1 = "d522c467aad17fd927e0db0130d2849a321a36aa",
+    )
+
+    maven_jar(
+        name = "docker-java-transport-zerodep",
+        artifact = "com.github.docker-java:docker-java-transport-zerodep:" + DOCKER_JAVA_VERS,
+        sha1 = "549f4985f9c7714deff47d1041603e85e132d184",
     )
 
     maven_jar(
@@ -122,6 +242,6 @@
 
     maven_jar(
         name = "jna",
-        artifact = "net.java.dev.jna:jna:5.5.0",
-        sha1 = "0e0845217c4907822403912ad6828d8e0b256208",
+        artifact = "net.java.dev.jna:jna:5.18.1",
+        sha1 = "b27ba04287cc4abe769642fe8318d39fc89bf937",
     )
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/AmazonDynamoDBProvider.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/AmazonDynamoDBProvider.java
deleted file mode 100644
index eb13649..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/AmazonDynamoDBProvider.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb;
-
-import com.amazonaws.auth.AWSCredentialsProvider;
-import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
-import com.amazonaws.auth.profile.ProfileCredentialsProvider;
-import com.amazonaws.client.builder.AwsClientBuilder;
-import com.amazonaws.regions.DefaultAwsRegionProviderChain;
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import software.amazon.awssdk.regions.Region;
-
-@Singleton
-class AmazonDynamoDBProvider implements Provider<AmazonDynamoDB> {
-  private final Configuration configuration;
-
-  @Inject
-  AmazonDynamoDBProvider(Configuration configuration) {
-    this.configuration = configuration;
-  }
-
-  @Override
-  public AmazonDynamoDB get() {
-    AmazonDynamoDBClientBuilder builder = AmazonDynamoDBClientBuilder.standard();
-    String region =
-        configuration
-            .getRegion()
-            .map(Region::id)
-            .orElseGet(() -> new DefaultAwsRegionProviderChain().getRegion());
-
-    configuration
-        .getEndpoint()
-        .ifPresent(
-            endpoint ->
-                builder.withEndpointConfiguration(
-                    new AwsClientBuilder.EndpointConfiguration(endpoint.toASCIIString(), region)));
-    return builder.withCredentials(getCredentialsProvider()).build();
-  }
-
-  private AWSCredentialsProvider getCredentialsProvider() {
-    return configuration
-        .getAwsConfigurationProfileName()
-        .<AWSCredentialsProvider>map(ProfileCredentialsProvider::new)
-        .orElse(new DefaultAWSCredentialsProviderChain());
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLifeCycleManager.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLifeCycleManager.java
index 1003164..455beea 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLifeCycleManager.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLifeCycleManager.java
@@ -1,51 +1,35 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb;
 
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.DynamoDBRefDatabase.LOCK_DB_PRIMARY_KEY;
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.DynamoDBRefDatabase.LOCK_DB_SORT_KEY;
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.DynamoDBRefDatabase.REF_DB_PRIMARY_KEY;
 
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDBLockClient;
-import com.amazonaws.services.dynamodbv2.CreateDynamoDBTableOptions;
-import com.amazonaws.services.dynamodbv2.document.Table;
-import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
-import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
-import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
-import com.amazonaws.services.dynamodbv2.model.KeyType;
-import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
-import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException;
-import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
-import com.amazonaws.services.dynamodbv2.util.TableUtils;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
+import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
+import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
+import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
+import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
+import software.amazon.awssdk.services.dynamodb.model.KeyType;
+import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
+import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
+import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
+import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;
 
 @Singleton
 class DynamoDBLifeCycleManager implements LifecycleListener {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private final Configuration configuration;
-  private final AmazonDynamoDB dynamoDB;
+  private final DynamoDbClient dynamoDbClient;
 
   @Inject
-  DynamoDBLifeCycleManager(Configuration configuration, AmazonDynamoDB dynamoDB) {
+  DynamoDBLifeCycleManager(Configuration configuration, DynamoDbClient dynamoDbClient) {
     this.configuration = configuration;
-    this.dynamoDB = dynamoDB;
+    this.dynamoDbClient = dynamoDbClient;
   }
 
   // TODO: it is useful to create these at start up during development
@@ -59,72 +43,110 @@
   }
 
   @Override
-  public void stop() {}
+  public void stop() {
+    // No-op. The dynamoDbClient lifecycle is managed by the provider.
+  }
 
   private void createLockTableIfDoesntExist() {
-    if (!tableExists(dynamoDB, configuration.getLocksTableName())) {
-      logger.atWarning().log(
-          "Attempt to create lock table '%s'", configuration.getLocksTableName());
-      AmazonDynamoDBLockClient.createLockTableInDynamoDB(
-          CreateDynamoDBTableOptions.builder(
-                  dynamoDB, new ProvisionedThroughput(10L, 10L), configuration.getLocksTableName())
-              .withPartitionKeyName(LOCK_DB_PRIMARY_KEY)
-              .withSortKeyName(LOCK_DB_SORT_KEY)
-              .build());
+    String tableName = configuration.getLocksTableName();
+    if (tableExists(dynamoDbClient, tableName)) {
+      logger.atFine().log("Lock table '%s' already exists, nothing to do.", tableName);
+      return;
+    }
 
-      try {
-        logger.atWarning().log(
-            "Wait for lock table '%s' creation", configuration.getLocksTableName());
-        TableUtils.waitUntilActive(dynamoDB, configuration.getLocksTableName());
-        logger.atWarning().log(
-            "lock table '%s' successfully created and active", configuration.getLocksTableName());
-      } catch (InterruptedException e) {
-        logger.atSevere().withCause(e).log(
-            "Timeout when creating lock table '%s'", configuration.getLocksTableName());
+    logger.atInfo().log("Attempting to create lock table '%s'", tableName);
+    try {
+      CreateTableRequest request =
+          CreateTableRequest.builder()
+              .tableName(tableName)
+              .attributeDefinitions(
+                  AttributeDefinition.builder()
+                      .attributeName(LOCK_DB_PRIMARY_KEY)
+                      .attributeType(ScalarAttributeType.S)
+                      .build(),
+                  AttributeDefinition.builder()
+                      .attributeName(LOCK_DB_SORT_KEY)
+                      .attributeType(ScalarAttributeType.S)
+                      .build())
+              .keySchema(
+                  KeySchemaElement.builder()
+                      .attributeName(LOCK_DB_PRIMARY_KEY)
+                      .keyType(KeyType.HASH)
+                      .build(),
+                  KeySchemaElement.builder()
+                      .attributeName(LOCK_DB_SORT_KEY)
+                      .keyType(KeyType.RANGE)
+                      .build())
+              .provisionedThroughput(
+                  ProvisionedThroughput.builder()
+                      .readCapacityUnits(10L)
+                      .writeCapacityUnits(10L)
+                      .build())
+              .build();
+
+      dynamoDbClient.createTable(request);
+
+      // Use a waiter to block until the table is active.
+      try (DynamoDbWaiter waiter = dynamoDbClient.waiter()) {
+        logger.atInfo().log("Waiting for lock table '%s' to become active...", tableName);
+        waiter.waitUntilTableExists(DescribeTableRequest.builder().tableName(tableName).build());
+        logger.atInfo().log("Lock table '%s' successfully created and active.", tableName);
       }
-    } else {
-      logger.atWarning().log(
-          "Lock table '%s' already exists, nothing to do.", configuration.getLocksTableName());
+    } catch (Exception e) {
+      // Catching a broad exception as waiter can throw different things.
+      logger.atSevere().withCause(e).log("Failed to create or wait for lock table '%s'", tableName);
     }
   }
 
   private void createRefsDbTableIfDoesntExist() {
-    boolean created =
-        TableUtils.createTableIfNotExists(
-            dynamoDB,
-            new CreateTableRequest()
-                .withTableName(configuration.getRefsDbTableName())
-                .withAttributeDefinitions(
-                    new AttributeDefinition(REF_DB_PRIMARY_KEY, ScalarAttributeType.S))
-                .withKeySchema(new KeySchemaElement(REF_DB_PRIMARY_KEY, KeyType.HASH))
-                .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)));
+    String tableName = configuration.getRefsDbTableName();
+    if (tableExists(dynamoDbClient, tableName)) {
+      logger.atFine().log("RefsDb table '%s' already exists, nothing to do.", tableName);
+      return;
+    }
 
-    if (created) {
-      try {
-        logger.atWarning().log(
-            "Wait for refsDB table '%s' creation", configuration.getRefsDbTableName());
-        TableUtils.waitUntilActive(dynamoDB, configuration.getRefsDbTableName());
-        logger.atWarning().log(
-            "refsDb table '%s' successfully created and active",
-            configuration.getRefsDbTableName());
-      } catch (InterruptedException e) {
-        logger.atSevere().withCause(e).log(
-            "Timeout when creating refsDb table '%s'", configuration.getRefsDbTableName());
+    logger.atInfo().log("Attempting to create refsDb table '%s'", tableName);
+    try {
+      CreateTableRequest request =
+          CreateTableRequest.builder()
+              .tableName(tableName)
+              .attributeDefinitions(
+                  AttributeDefinition.builder()
+                      .attributeName(REF_DB_PRIMARY_KEY)
+                      .attributeType(ScalarAttributeType.S)
+                      .build())
+              .keySchema(
+                  KeySchemaElement.builder()
+                      .attributeName(REF_DB_PRIMARY_KEY)
+                      .keyType(KeyType.HASH)
+                      .build())
+              .provisionedThroughput(
+                  ProvisionedThroughput.builder()
+                      .readCapacityUnits(10L)
+                      .writeCapacityUnits(10L)
+                      .build())
+              .build();
+
+      dynamoDbClient.createTable(request);
+
+      try (DynamoDbWaiter waiter = dynamoDbClient.waiter()) {
+        logger.atInfo().log("Waiting for refsDb table '%s' to become active...", tableName);
+        waiter.waitUntilTableExists(DescribeTableRequest.builder().tableName(tableName).build());
+        logger.atInfo().log("RefsDb table '%s' successfully created and active.", tableName);
       }
-    } else {
-      logger.atWarning().log(
-          "RefsDb table '%s' already exists, nothing to do.", configuration.getRefsDbTableName());
+    } catch (Exception e) {
+      logger.atSevere().withCause(e).log(
+          "Failed to create or wait for refsDb table '%s'", tableName);
     }
   }
 
   @VisibleForTesting
-  static boolean tableExists(AmazonDynamoDB dynamoDB, String tableName) {
-    final Table table = new Table(dynamoDB, tableName);
+  static boolean tableExists(DynamoDbClient dynamoDbClient, String tableName) {
     try {
-      table.describe();
+      dynamoDbClient.describeTable(DescribeTableRequest.builder().tableName(tableName).build());
+      return true;
     } catch (ResourceNotFoundException e) {
       return false;
     }
-    return true;
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLockClientProvider.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLockClientProvider.java
index 42009f1..b94629e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLockClientProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBLockClientProvider.java
@@ -17,30 +17,31 @@
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.DynamoDBRefDatabase.LOCK_DB_PRIMARY_KEY;
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.DynamoDBRefDatabase.LOCK_DB_SORT_KEY;
 
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
 import com.amazonaws.services.dynamodbv2.AmazonDynamoDBLockClient;
 import com.amazonaws.services.dynamodbv2.AmazonDynamoDBLockClientOptions;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.util.concurrent.TimeUnit;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 @Singleton
 class DynamoDBLockClientProvider implements Provider<AmazonDynamoDBLockClient> {
   private final Configuration configuration;
-  private final AmazonDynamoDB dynamoDB;
+  private final DynamoDbClient dynamoDbClient;
 
   @Inject
-  DynamoDBLockClientProvider(Configuration configuration, AmazonDynamoDB dynamoDB) {
+  DynamoDBLockClientProvider(Configuration configuration, DynamoDbClient dynamoDbClient) {
     this.configuration = configuration;
-    this.dynamoDB = dynamoDB;
+    this.dynamoDbClient = dynamoDbClient;
   }
 
   @Override
   public AmazonDynamoDBLockClient get() {
     final boolean createHeartbeatBackgroundThread = true;
+
     return new AmazonDynamoDBLockClient(
-        AmazonDynamoDBLockClientOptions.builder(dynamoDB, configuration.getLocksTableName())
+        AmazonDynamoDBLockClientOptions.builder(dynamoDbClient, configuration.getLocksTableName())
             .withPartitionKeyName(LOCK_DB_PRIMARY_KEY)
             .withSortKeyName(LOCK_DB_SORT_KEY)
             .withTimeUnit(TimeUnit.SECONDS)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabase.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabase.java
index 712f5d1..68ac4e7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabase.java
@@ -17,22 +17,14 @@
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.ProjectVersionCacheModule.PROJECT_VERSION_CACHE;
 
 import com.amazonaws.services.dynamodbv2.AcquireLockOptions;
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
 import com.amazonaws.services.dynamodbv2.AmazonDynamoDBLockClient;
 import com.amazonaws.services.dynamodbv2.LockItem;
-import com.amazonaws.services.dynamodbv2.model.AttributeAction;
-import com.amazonaws.services.dynamodbv2.model.AttributeValue;
-import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
-import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException;
-import com.amazonaws.services.dynamodbv2.model.GetItemResult;
 import com.amazonaws.services.dynamodbv2.model.LockNotGrantedException;
-import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest;
 import com.gerritforge.gerrit.globalrefdb.ExtendedGlobalRefDatabase;
 import com.gerritforge.gerrit.globalrefdb.GlobalRefDbLockException;
 import com.gerritforge.gerrit.globalrefdb.GlobalRefDbSystemError;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Project;
@@ -40,11 +32,19 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.name.Named;
+import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import javax.inject.Singleton;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
+import software.amazon.awssdk.core.exception.SdkException;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
+import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
+import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
+import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
+import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
+import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
 
 @Singleton
 public class DynamoDBRefDatabase implements ExtendedGlobalRefDatabase {
@@ -57,14 +57,14 @@
 
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private final AmazonDynamoDBLockClient lockClient;
-  private final AmazonDynamoDB dynamoDBClient;
+  private final DynamoDbClient dynamoDBClient;
   private final Configuration configuration;
   private final LoadingCache<String, Optional<Integer>> projectVersionCache;
 
   @Inject
   DynamoDBRefDatabase(
       AmazonDynamoDBLockClient lockClient,
-      AmazonDynamoDB dynamoDBClient,
+      DynamoDbClient dynamoDBClient,
       Configuration configuration,
       @Named(PROJECT_VERSION_CACHE) LoadingCache<String, Optional<Integer>> projectVersionCache) {
     this.lockClient = lockClient;
@@ -88,12 +88,12 @@
   @Override
   public boolean isUpToDate(Project.NameKey project, Ref ref) throws GlobalRefDbLockException {
     try {
-      GetItemResult result = getItemFromDynamoDB(pathFor(project, ref.getName()));
-      if (!exists(result)) {
+      GetItemResponse response = getItemFromDynamoDB(pathFor(project, ref.getName()));
+      if (!response.hasItem()) {
         return true;
       }
 
-      String valueInDynamoDB = result.getItem().get(REF_DB_VALUE_KEY).getS();
+      String valueInDynamoDB = response.item().get(REF_DB_VALUE_KEY).s();
       ObjectId objectIdInSharedRefDb = ObjectId.fromString(valueInDynamoDB);
       boolean isUpToDate = objectIdInSharedRefDb.equals(ref.getObjectId());
 
@@ -134,19 +134,22 @@
   private boolean doCompareAndPut(
       Project.NameKey project, String refPath, String currValueForPath, String newValueForPath)
       throws GlobalRefDbSystemError {
+    Map<String, AttributeValue> key = getKey(refPath);
+    Map<String, AttributeValue> expressionAttributeValues =
+        Map.of(
+            ":old_value", AttributeValue.builder().s(currValueForPath).build(),
+            ":new_value", AttributeValue.builder().s(newValueForPath).build());
     UpdateItemRequest updateItemRequest =
-        new UpdateItemRequest()
-            .withTableName(configuration.getRefsDbTableName())
-            .withKey(ImmutableMap.of(REF_DB_PRIMARY_KEY, new AttributeValue(refPath)))
-            .withExpressionAttributeValues(
-                ImmutableMap.of(
-                    ":old_value", new AttributeValue(currValueForPath),
-                    ":new_value", new AttributeValue(newValueForPath)))
-            .withUpdateExpression(String.format("SET %s = %s", REF_DB_VALUE_KEY, ":new_value"))
-            .withConditionExpression(
+        UpdateItemRequest.builder()
+            .tableName(configuration.getRefsDbTableName())
+            .key(key)
+            .expressionAttributeValues(expressionAttributeValues)
+            .updateExpression(String.format("SET %s = :new_value", REF_DB_VALUE_KEY))
+            .conditionExpression(
                 String.format(
                     "attribute_not_exists(%s) OR %s = :old_value",
-                    REF_DB_PRIMARY_KEY, REF_DB_VALUE_KEY));
+                    REF_DB_PRIMARY_KEY, REF_DB_VALUE_KEY))
+            .build();
     try {
       dynamoDBClient.updateItem(updateItemRequest);
       logger.atFine().log(
@@ -158,7 +161,7 @@
           "Conditional Check Failure when updating refPath %s. expected: %s New: %s",
           refPath, currValueForPath, newValueForPath);
       return false;
-    } catch (Exception e) {
+    } catch (SdkException e) {
       throw new GlobalRefDbSystemError(
           String.format(
               "Error updating refPath %s. expected: %s new: %s",
@@ -175,16 +178,21 @@
   public <T> void doPut(NameKey project, String refPath, T value) throws GlobalRefDbSystemError {
     String refValue =
         Optional.ofNullable(value).map(Object::toString).orElse(ObjectId.zeroId().getName());
+    Map<String, AttributeValue> key = getKey(refPath);
+    Map<String, AttributeValue> expressionAttributeValues =
+        Map.of(":val", AttributeValue.builder().s(refValue).build());
+    UpdateItemRequest request =
+        UpdateItemRequest.builder()
+            .tableName(configuration.getRefsDbTableName())
+            .key(key)
+            .updateExpression(String.format("SET %s = :val", REF_DB_VALUE_KEY))
+            .expressionAttributeValues(expressionAttributeValues)
+            .build();
     try {
-      dynamoDBClient.updateItem(
-          configuration.getRefsDbTableName(),
-          ImmutableMap.of(REF_DB_PRIMARY_KEY, new AttributeValue(refPath)),
-          ImmutableMap.of(
-              REF_DB_VALUE_KEY,
-              new AttributeValueUpdate(new AttributeValue(refValue), AttributeAction.PUT)));
+      dynamoDBClient.updateItem(request);
       logger.atFine().log(
           "Updated path for project %s, path %s, value: %s", project.get(), refPath, refValue);
-    } catch (Exception e) {
+    } catch (SdkException e) {
       throw new GlobalRefDbSystemError(
           String.format(
               "Error updating path for project %s, path %s. value: %s",
@@ -258,11 +266,11 @@
   public <T> Optional<T> get(Project.NameKey project, String refName, Class<T> clazz)
       throws GlobalRefDbSystemError {
     try {
-      GetItemResult item = getItemFromDynamoDB(pathFor(project, refName));
-      if (!exists(item)) {
+      GetItemResponse reponse = getItemFromDynamoDB(pathFor(project, refName));
+      if (!reponse.hasItem()) {
         return Optional.empty();
       }
-      String refValue = item.getItem().get(REF_DB_VALUE_KEY).getS();
+      String refValue = reponse.item().get(REF_DB_VALUE_KEY).s();
 
       // TODO: not every string might be cast to T (it should work now because the
       // only usage of this function requests string, but we should be serializing
@@ -274,19 +282,27 @@
     }
   }
 
-  private GetItemResult getItemFromDynamoDB(String refPath) {
+  private GetItemResponse getItemFromDynamoDB(String refPath) {
     return getItemFromDynamoDB(refPath, true);
   }
 
-  public GetItemResult getItemFromDynamoDB(String refPath, Boolean consistentRead) {
-    return dynamoDBClient.getItem(
-        configuration.getRefsDbTableName(),
-        ImmutableMap.of(REF_DB_PRIMARY_KEY, new AttributeValue(refPath)),
-        consistentRead);
+  public GetItemResponse getItemFromDynamoDB(String refPath, Boolean consistentRead) {
+    Map<String, AttributeValue> key = getKey(refPath);
+    GetItemRequest request =
+        GetItemRequest.builder()
+            .tableName(configuration.getRefsDbTableName())
+            .key(key)
+            .consistentRead(consistentRead)
+            .build();
+    return dynamoDBClient.getItem(request);
   }
 
-  public boolean exists(GetItemResult result) {
-    return result.getItem() != null && !result.getItem().isEmpty();
+  public Map<String, AttributeValue> getKey(String refPath) {
+    return Map.of(REF_DB_PRIMARY_KEY, AttributeValue.builder().s(refPath).build());
+  }
+
+  public boolean exists(GetItemResponse response) {
+    return response.hasItem() && !response.item().isEmpty();
   }
 
   static class ProjectVersionCacheLoader extends CacheLoader<String, Optional<Integer>> {
@@ -300,13 +316,13 @@
 
     @Override
     public Optional<Integer> load(String project) throws Exception {
-      GetItemResult item =
+      GetItemResponse item =
           dynamoDBRefDatabaseProvider
               .get()
               .getItemFromDynamoDB(currentVersionKey(Project.nameKey(project)), false);
       Integer currentVersion =
           dynamoDBRefDatabaseProvider.get().exists(item)
-              ? Integer.parseInt(item.getItem().get(REF_DB_VALUE_KEY).getS())
+              ? Integer.parseInt(item.item().get(REF_DB_VALUE_KEY).s())
               : null;
       return Optional.ofNullable(currentVersion);
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDbClientProvider.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDbClientProvider.java
new file mode 100644
index 0000000..05b15b0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDbClientProvider.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
+import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder;
+
+@Singleton
+class DynamoDbClientProvider implements Provider<DynamoDbClient> {
+  private final Configuration configuration;
+
+  @Inject
+  DynamoDbClientProvider(Configuration configuration) {
+    this.configuration = configuration;
+  }
+
+  @Override
+  public DynamoDbClient get() {
+    DynamoDbClientBuilder builder =
+        DynamoDbClient.builder()
+            .credentialsProvider(getCredentialsProvider())
+            .region(
+                configuration
+                    .getRegion()
+                    .orElseGet(() -> new DefaultAwsRegionProviderChain().getRegion()));
+    configuration.getEndpoint().ifPresent(builder::endpointOverride);
+
+    return builder.build();
+  }
+
+  private AwsCredentialsProvider getCredentialsProvider() {
+    return configuration
+        .getAwsConfigurationProfileName()
+        .<AwsCredentialsProvider>map(
+            profileName -> ProfileCredentialsProvider.builder().profileName(profileName).build())
+        .orElseGet(() -> DefaultCredentialsProvider.builder().build());
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/Module.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/Module.java
index 4def9f6..a5a4b4e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/Module.java
@@ -16,13 +16,13 @@
 
 import static com.google.inject.Scopes.SINGLETON;
 
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
 import com.amazonaws.services.dynamodbv2.AmazonDynamoDBLockClient;
 import com.gerritforge.gerrit.globalrefdb.GlobalRefDatabase;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.inject.Scopes;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 class Module extends LifecycleModule {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -34,7 +34,7 @@
         .to(DynamoDBRefDatabase.class)
         .in(Scopes.SINGLETON);
     install(new ProjectVersionCacheModule());
-    bind(AmazonDynamoDB.class).toProvider(AmazonDynamoDBProvider.class).in(SINGLETON);
+    bind(DynamoDbClient.class).toProvider(DynamoDbClientProvider.class).in(SINGLETON);
     bind(AmazonDynamoDBLockClient.class).toProvider(DynamoDBLockClientProvider.class).in(SINGLETON);
     listener().to(DynamoDBLifeCycleManager.class);
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabaseIT.java b/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabaseIT.java
index 246ff79..c39d0b3 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabaseIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/dynamodb/DynamoDBRefDatabaseIT.java
@@ -19,9 +19,9 @@
 import static com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.Configuration.DEFAULT_REFS_DB_TABLE_NAME;
 import static org.testcontainers.containers.localstack.LocalStackContainer.Service.DYNAMODB;
 
-import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
 import com.gerritforge.gerrit.globalrefdb.GlobalRefDbLockException;
 import com.google.common.cache.LoadingCache;
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.WaitUtil;
@@ -35,49 +35,57 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.Ref;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.testcontainers.containers.localstack.LocalStackContainer;
 import org.testcontainers.utility.DockerImageName;
+import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 @TestPlugin(
     name = "aws-dynamodb-refdb",
     sysModule = "com.googlesource.gerrit.plugins.validation.dfsrefdb.dynamodb.Module")
 public class DynamoDBRefDatabaseIT extends LightweightPluginDaemonTest {
-  private static final Duration DYNAMODB_TABLE_CREATION_TIMEOUT = Duration.ofSeconds(10);
-
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private static final int LOCALSTACK_PORT = 4566;
-  private static final LocalStackContainer localstack =
-      new LocalStackContainer(DockerImageName.parse("localstack/localstack:0.12.8"))
-          .withServices(DYNAMODB)
-          .withExposedPorts(LOCALSTACK_PORT);
+  private static final Duration DYNAMODB_TABLE_CREATION_TIMEOUT = Duration.ofSeconds(10);
+  private static LocalStackContainer localstack;
+
+  @BeforeClass
+  public static void setupLocalStack() {
+    logger.atFine().log("--- Testcontainers Debug Info ---");
+    logger.atFine().log("DOCKER_HOST (env): %s", System.getenv("DOCKER_HOST"));
+    logger.atFine().log("docker.host (prop): %s", System.getProperty("docker.host"));
+    logger.atFine().log("---------------------------------");
+
+    localstack =
+        new LocalStackContainer(DockerImageName.parse("localstack/localstack").withTag("4.9.2"))
+            .withServices(DYNAMODB)
+            .withExposedPorts(LOCALSTACK_PORT);
+    localstack.start();
+  }
+
+  @AfterClass
+  public static void tearDown() {
+    localstack.close();
+  }
 
   @Before
   @Override
   public void setUpTestPlugin() throws Exception {
-    localstack.start();
-
     System.setProperty("endpoint", localstack.getEndpointOverride(DYNAMODB).toASCIIString());
     System.setProperty("region", localstack.getRegion());
-    System.setProperty("aws.accessKeyId", localstack.getAccessKey());
 
-    // The secret key property name has changed from aws-sdk 1.11.x and 2.x [1]
-    // Export both names so that default credential provider chains work regardless
-    // he underlying library version.
-    // https: // docs.aws.amazon.com/sdk-for-java/latest/migration-guide/client-credential.html
-    System.setProperty("aws.secretKey", localstack.getSecretKey());
+    // localstack recommends to set both access key id and secret access key to the same value
+    // it ignores the secret access key
+    // see https://docs.localstack.cloud/aws/capabilities/config/credentials/
+    System.setProperty("aws.accessKeyId", localstack.getSecretKey());
     System.setProperty("aws.secretAccessKey", localstack.getSecretKey());
 
     super.setUpTestPlugin();
   }
 
-  @Override
-  public void tearDownTestPlugin() {
-    localstack.close();
-
-    super.tearDownTestPlugin();
-  }
-
   @Test
   public void shouldEnsureLockTableExists() throws Exception {
     WaitUtil.waitUntil(
@@ -276,8 +284,8 @@
     assertThat(dynamoDBRefDatabase().getCurrentVersion(project)).isNull();
   }
 
-  private AmazonDynamoDB dynamoDBClient() {
-    return plugin.getSysInjector().getInstance(AmazonDynamoDB.class);
+  private DynamoDbClient dynamoDBClient() {
+    return plugin.getSysInjector().getInstance(DynamoDbClient.class);
   }
 
   private DynamoDBRefDatabase dynamoDBRefDatabase() {
diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties
new file mode 100644
index 0000000..099992b
--- /dev/null
+++ b/src/test/resources/simplelogger.properties
@@ -0,0 +1,9 @@
+# Set the default log level to TRACE to see everything
+org.slf4j.simpleLogger.defaultLogLevel = trace
+
+# Show thread names
+org.slf4j.simpleLogger.showThreadName = true
+
+# Show date and time
+org.slf4j.simpleLogger.showDateTime = true
+org.slf4j.simpleLogger.dateTimeFormat = yyyy-MM-dd HH:mm:ss:SSS Z
\ No newline at end of file