This document provides comprehensive examples for integrating LocalStack with AWS SDK v1 and v2 for Java, including error handling, edge cases, and complete working examples.
AWS SDK v2 is the modern, modular SDK for Java with improved performance and developer experience.
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.S3Exception;
LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:2.0")
)
.withServices(Service.S3);
localstack.start();
try {
S3Client s3 = S3Client.builder()
.endpointOverride(localstack.getEndpoint())
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.region(Region.of(localstack.getRegion()))
.build();
// Use the client
s3.createBucket(b -> b.bucket("test-bucket"));
} catch (S3Exception e) {
System.err.println("S3 error: " + e.getMessage());
throw e;
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
} finally {
localstack.stop();
}import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:2.0")
)
.withServices(Service.S3, Service.SQS, Service.DYNAMODB);
localstack.start();
try {
// Shared configuration
StaticCredentialsProvider credentialsProvider =
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
);
Region region = Region.of(localstack.getRegion());
URI endpoint = localstack.getEndpoint();
// Create S3 client
S3Client s3 = S3Client.builder()
.endpointOverride(endpoint)
.credentialsProvider(credentialsProvider)
.region(region)
.build();
// Create SQS client
SqsClient sqs = SqsClient.builder()
.endpointOverride(endpoint)
.credentialsProvider(credentialsProvider)
.region(region)
.build();
// Create DynamoDB client
DynamoDbClient dynamodb = DynamoDbClient.builder()
.endpointOverride(endpoint)
.credentialsProvider(credentialsProvider)
.region(region)
.build();
// Use the clients
s3.createBucket(b -> b.bucket("test-bucket"));
sqs.createQueue(q -> q.queueName("test-queue"));
} finally {
localstack.stop();
}import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.services.s3.model.S3Exception;
S3Client s3 = // ... configured as above
try {
// Create bucket
s3.createBucket(b -> b.bucket("my-bucket"));
// Put object
s3.putObject(
p -> p.bucket("my-bucket").key("document.txt"),
RequestBody.fromString("Hello from LocalStack!")
);
// Get object
String content = s3.getObject(
g -> g.bucket("my-bucket").key("document.txt"),
ResponseTransformer.toBytes()
).asUtf8String();
// List buckets
ListBucketsResponse bucketsResponse = s3.listBuckets();
bucketsResponse.buckets().forEach(bucket -> {
System.out.println("Bucket: " + bucket.name());
});
// List objects
ListObjectsV2Response objectsResponse = s3.listObjectsV2(
l -> l.bucket("my-bucket")
);
objectsResponse.contents().forEach(object -> {
System.out.println("Object: " + object.key());
});
// Delete object
s3.deleteObject(d -> d.bucket("my-bucket").key("document.txt"));
// Delete bucket (must be empty)
s3.deleteBucket(d -> d.bucket("my-bucket"));
} catch (NoSuchBucketException e) {
System.err.println("Bucket does not exist: " + e.getMessage());
} catch (NoSuchKeyException e) {
System.err.println("Object does not exist: " + e.getMessage());
} catch (S3Exception e) {
System.err.println("S3 error: " + e.awsErrorDetails().errorMessage());
throw e;
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
}LocalStack requires path-style access for S3. The getEndpoint() method returns an IP address to ensure path-style access works correctly:
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
LocalStackContainer localstack = // ... configured
localstack.start();
URI endpoint = localstack.getEndpoint();
// Returns IP address like: http://192.168.1.100:49153
// NOT hostname like: http://localhost:49153
S3Client s3 = S3Client.builder()
.endpointOverride(endpoint) // IP address ensures path-style access
.forcePathStyle(true) // Explicitly enable path-style (recommended)
.credentialsProvider(/* ... */)
.region(Region.of(localstack.getRegion()))
.build();
// This works correctly with path-style access
s3.putObject(
PutObjectRequest.builder()
.bucket("my-bucket")
.key("my-key")
.build(),
RequestBody.fromString("content")
);import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import software.amazon.awssdk.core.exception.SdkClientException;
SqsClient sqs = // ... configured as above
try {
// Create queue
CreateQueueResponse createResponse = sqs.createQueue(
c -> c.queueName("my-queue")
);
String queueUrl = createResponse.queueUrl();
// Send message
sqs.sendMessage(m -> m
.queueUrl(queueUrl)
.messageBody("Test message")
);
// Receive messages
ReceiveMessageResponse receiveResponse = sqs.receiveMessage(
r -> r.queueUrl(queueUrl).maxNumberOfMessages(10)
);
receiveResponse.messages().forEach(message -> {
System.out.println("Message: " + message.body());
// Delete message after processing
sqs.deleteMessage(d -> d
.queueUrl(queueUrl)
.receiptHandle(message.receiptHandle())
);
});
// Delete queue
sqs.deleteQueue(d -> d.queueUrl(queueUrl));
} catch (QueueDoesNotExistException e) {
System.err.println("Queue does not exist: " + e.getMessage());
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
}import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.core.exception.SdkClientException;
import java.util.Map;
DynamoDbClient dynamodb = // ... configured as above
try {
// Create table
dynamodb.createTable(CreateTableRequest.builder()
.tableName("Users")
.keySchema(
KeySchemaElement.builder()
.attributeName("userId")
.keyType(KeyType.HASH)
.build()
)
.attributeDefinitions(
AttributeDefinition.builder()
.attributeName("userId")
.attributeType(ScalarAttributeType.S)
.build()
)
.billingMode(BillingMode.PAY_PER_REQUEST)
.build()
);
// Wait for table to be active (optional but recommended)
dynamodb.waiter().waitUntilTableExists(
b -> b.tableName("Users")
);
// Put item
dynamodb.putItem(PutItemRequest.builder()
.tableName("Users")
.item(Map.of(
"userId", AttributeValue.builder().s("user123").build(),
"name", AttributeValue.builder().s("John Doe").build(),
"email", AttributeValue.builder().s("john@example.com").build()
))
.build()
);
// Get item
GetItemResponse getResponse = dynamodb.getItem(GetItemRequest.builder()
.tableName("Users")
.key(Map.of("userId", AttributeValue.builder().s("user123").build()))
.build()
);
if (getResponse.hasItem()) {
Map<String, AttributeValue> item = getResponse.item();
String name = item.get("name").s();
System.out.println("User name: " + name);
}
// Scan table
ScanResponse scanResponse = dynamodb.scan(
s -> s.tableName("Users")
);
scanResponse.items().forEach(userItem -> {
System.out.println("User: " + userItem.get("userId").s());
});
} catch (ResourceInUseException e) {
System.err.println("Table already exists: " + e.getMessage());
} catch (ResourceNotFoundException e) {
System.err.println("Table does not exist: " + e.getMessage());
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
}import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.*;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.core.exception.SdkClientException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
LambdaClient lambda = // ... configured as above
try {
// Create function (requires zip file)
byte[] zipBytes = Files.readAllBytes(Paths.get("function.zip"));
lambda.createFunction(CreateFunctionRequest.builder()
.functionName("my-function")
.runtime(Runtime.PYTHON3_11)
.handler("index.handler")
.role("arn:aws:iam::000000000000:role/lambda-role")
.code(FunctionCode.builder()
.zipFile(SdkBytes.fromByteArray(zipBytes))
.build())
.build()
);
// Wait for function to be active
lambda.waiter().waitUntilFunctionActive(
b -> b.functionName("my-function")
);
// Invoke function
InvokeResponse invokeResponse = lambda.invoke(InvokeRequest.builder()
.functionName("my-function")
.payload(SdkBytes.fromUtf8String("{\"key\": \"value\"}"))
.build()
);
String result = invokeResponse.payload().asUtf8String();
System.out.println("Lambda result: " + result);
} catch (ResourceConflictException e) {
System.err.println("Function already exists: " + e.getMessage());
} catch (ResourceNotFoundException e) {
System.err.println("Function does not exist: " + e.getMessage());
} catch (IOException e) {
System.err.println("Failed to read function zip: " + e.getMessage());
throw e;
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
}AWS SDK v1 is the legacy SDK, still widely used in existing projects.
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.AmazonClientException;
import com.amazonaws.services.s3.model.AmazonS3Exception;
LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:2.0")
)
.withServices(Service.S3);
localstack.start();
try {
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration(
localstack.getEndpoint().toString(),
localstack.getRegion()
)
)
.withCredentials(
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.withPathStyleAccessEnabled(true) // Required for LocalStack
.build();
// Use the client
s3.createBucket("test-bucket");
} catch (AmazonS3Exception e) {
System.err.println("S3 error: " + e.getMessage());
throw e;
} catch (AmazonClientException e) {
System.err.println("AWS client error: " + e.getMessage());
throw e;
} finally {
localstack.stop();
}import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:2.0")
)
.withServices(Service.S3, Service.SQS, Service.DYNAMODB);
localstack.start();
try {
// Shared configuration
AwsClientBuilder.EndpointConfiguration endpointConfig =
new AwsClientBuilder.EndpointConfiguration(
localstack.getEndpoint().toString(),
localstack.getRegion()
);
AWSStaticCredentialsProvider credentialsProvider =
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
localstack.getAccessKey(),
localstack.getSecretKey()
)
);
// Create S3 client
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(endpointConfig)
.withCredentials(credentialsProvider)
.withPathStyleAccessEnabled(true)
.build();
// Create SQS client
AmazonSQS sqs = AmazonSQSClientBuilder.standard()
.withEndpointConfiguration(endpointConfig)
.withCredentials(credentialsProvider)
.build();
// Create DynamoDB client
AmazonDynamoDB dynamodb = AmazonDynamoDBClientBuilder.standard()
.withEndpointConfiguration(endpointConfig)
.withCredentials(credentialsProvider)
.build();
// Use the clients
s3.createBucket("test-bucket");
sqs.createQueue("test-queue");
} finally {
localstack.stop();
}import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.AmazonClientException;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import org.apache.commons.io.IOUtils;
import java.nio.charset.StandardCharsets;
AmazonS3 s3 = // ... configured as above
try {
// Create bucket
s3.createBucket("my-bucket");
// Put object
s3.putObject("my-bucket", "document.txt", "Hello from LocalStack!");
// Get object
S3Object object = s3.getObject("my-bucket", "document.txt");
String content = IOUtils.toString(
object.getObjectContent(),
StandardCharsets.UTF_8
);
// List buckets
List<Bucket> buckets = s3.listBuckets();
buckets.forEach(bucket -> {
System.out.println("Bucket: " + bucket.getName());
});
// List objects
ObjectListing objectListing = s3.listObjects("my-bucket");
objectListing.getObjectSummaries().forEach(summary -> {
System.out.println("Object: " + summary.getKey());
});
// Delete object
s3.deleteObject("my-bucket", "document.txt");
// Delete bucket (must be empty)
s3.deleteBucket("my-bucket");
} catch (AmazonS3Exception e) {
if (e.getStatusCode() == 404) {
System.err.println("Bucket or object not found: " + e.getMessage());
} else {
System.err.println("S3 error: " + e.getMessage());
throw e;
}
} catch (AmazonClientException e) {
System.err.println("AWS client error: " + e.getMessage());
throw e;
}import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.model.*;
import com.amazonaws.AmazonClientException;
AmazonSQS sqs = // ... configured as above
try {
// Create queue
CreateQueueResult createResult = sqs.createQueue("my-queue");
String queueUrl = createResult.getQueueUrl();
// Send message
sqs.sendMessage(queueUrl, "Test message");
// Receive messages
ReceiveMessageResult receiveResult = sqs.receiveMessage(
new ReceiveMessageRequest(queueUrl)
.withMaxNumberOfMessages(10)
);
receiveResult.getMessages().forEach(message -> {
System.out.println("Message: " + message.getBody());
// Delete message after processing
sqs.deleteMessage(queueUrl, message.getReceiptHandle());
});
// Delete queue
sqs.deleteQueue(queueUrl);
} catch (QueueDoesNotExistException e) {
System.err.println("Queue does not exist: " + e.getMessage());
} catch (AmazonClientException e) {
System.err.println("AWS client error: " + e.getMessage());
throw e;
}import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.model.*;
import com.amazonaws.AmazonClientException;
import java.util.HashMap;
import java.util.Map;
AmazonDynamoDB dynamodb = // ... configured as above
try {
// Create table
CreateTableRequest createTableRequest = new CreateTableRequest()
.withTableName("Users")
.withKeySchema(new KeySchemaElement("userId", KeyType.HASH))
.withAttributeDefinitions(
new AttributeDefinition("userId", ScalarAttributeType.S)
)
.withBillingMode(BillingMode.PAY_PER_REQUEST);
dynamodb.createTable(createTableRequest);
// Wait for table to be active
TableDescription table = dynamodb.describeTable("Users").getTable();
while (!"ACTIVE".equals(table.getTableStatus())) {
Thread.sleep(1000);
table = dynamodb.describeTable("Users").getTable();
}
// Put item
Map<String, AttributeValue> item = new HashMap<>();
item.put("userId", new AttributeValue("user123"));
item.put("name", new AttributeValue("John Doe"));
item.put("email", new AttributeValue("john@example.com"));
dynamodb.putItem("Users", item);
// Get item
Map<String, AttributeValue> key = new HashMap<>();
key.put("userId", new AttributeValue("user123"));
GetItemResult getResult = dynamodb.getItem("Users", key);
if (getResult.getItem() != null) {
Map<String, AttributeValue> resultItem = getResult.getItem();
String name = resultItem.get("name").getS();
System.out.println("User name: " + name);
}
} catch (ResourceInUseException e) {
System.err.println("Table already exists: " + e.getMessage());
} catch (ResourceNotFoundException e) {
System.err.println("Table does not exist: " + e.getMessage());
} catch (AmazonClientException e) {
System.err.println("AWS client error: " + e.getMessage());
throw e;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted while waiting for table", e);
}import org.junit.ClassRule;
import org.junit.Test;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
public class AwsIntegrationTest {
@ClassRule
public static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:2.0")
)
.withServices(Service.S3, Service.SQS);
@Test
public void testS3Operations() {
S3Client s3 = S3Client.builder()
.endpointOverride(localstack.getEndpoint())
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.region(Region.of(localstack.getRegion()))
.build();
// Test code
s3.createBucket(b -> b.bucket("test-bucket"));
// ... assertions
}
}import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
@Testcontainers
public class AwsIntegrationTest {
@Container
static LocalStackContainer localstack = new LocalStackContainer(
DockerImageName.parse("localstack/localstack:2.0")
)
.withServices(Service.S3, Service.SQS);
@Test
void testS3Operations() {
S3Client s3 = S3Client.builder()
.endpointOverride(localstack.getEndpoint())
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.region(Region.of(localstack.getRegion()))
.build();
// Test code
s3.createBucket(b -> b.bucket("test-bucket"));
// ... assertions
}
}Create a reusable helper method to configure clients:
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import org.testcontainers.containers.localstack.LocalStackContainer;
public class LocalStackClientHelper {
public static S3Client createS3Client(LocalStackContainer localstack) {
return S3Client.builder()
.endpointOverride(localstack.getEndpoint())
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.region(Region.of(localstack.getRegion()))
.forcePathStyle(true)
.build();
}
public static SqsClient createSqsClient(LocalStackContainer localstack) {
return SqsClient.builder()
.endpointOverride(localstack.getEndpoint())
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.region(Region.of(localstack.getRegion()))
.build();
}
public static DynamoDbClient createDynamoDbClient(LocalStackContainer localstack) {
return DynamoDbClient.builder()
.endpointOverride(localstack.getEndpoint())
.credentialsProvider(
StaticCredentialsProvider.create(
AwsBasicCredentials.create(
localstack.getAccessKey(),
localstack.getSecretKey()
)
)
)
.region(Region.of(localstack.getRegion()))
.build();
}
}
// Usage
S3Client s3 = LocalStackClientHelper.createS3Client(localstack);
SqsClient sqs = LocalStackClientHelper.createSqsClient(localstack);import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
public void handleS3Operations(S3Client s3, String bucketName, String key) {
try {
// Create bucket
s3.createBucket(b -> b.bucket(bucketName));
} catch (BucketAlreadyExistsException e) {
System.out.println("Bucket already exists, continuing...");
} catch (BucketAlreadyOwnedByYouException e) {
System.out.println("Bucket already owned by you, continuing...");
} catch (S3Exception e) {
System.err.println("S3 error creating bucket: " + e.awsErrorDetails().errorMessage());
throw e;
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
}
try {
// Put object
s3.putObject(
p -> p.bucket(bucketName).key(key),
RequestBody.fromString("content")
);
} catch (NoSuchBucketException e) {
System.err.println("Bucket does not exist: " + bucketName);
throw e;
} catch (S3Exception e) {
System.err.println("S3 error putting object: " + e.awsErrorDetails().errorMessage());
throw e;
} catch (SdkClientException e) {
System.err.println("SDK client error: " + e.getMessage());
throw e;
}
}<dependencies>
<!-- Testcontainers LocalStack -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>localstack</artifactId>
<version>1.21.4</version>
<scope>test</scope>
</dependency>
<!-- AWS SDK v2 - S3 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.28.6</version>
<scope>test</scope>
</dependency>
<!-- AWS SDK v2 - SQS -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sqs</artifactId>
<version>2.28.6</version>
<scope>test</scope>
</dependency>
<!-- AWS SDK v2 - DynamoDB -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
<version>2.28.6</version>
<scope>test</scope>
</dependency>
</dependencies><dependencies>
<!-- Testcontainers LocalStack -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>localstack</artifactId>
<version>1.21.4</version>
<scope>test</scope>
</dependency>
<!-- AWS SDK v1 BOM -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>1.12.572</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- AWS SDK v1 - S3 -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<scope>test</scope>
</dependency>
<!-- AWS SDK v1 - SQS -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sqs</artifactId>
<scope>test</scope>
</dependency>
</dependencies>