0
# Common Patterns
1
2
The AWS SDK for Java 1.x uses consistent patterns across all 371+ AWS services. Understanding these patterns allows you to work with any service efficiently.
3
4
## Client Creation Pattern
5
6
All AWS service clients follow the same builder pattern for creation.
7
8
### Pattern: Default Client
9
10
```java
11
// Simplest approach - uses default credentials and region providers
12
AmazonS3 s3 = AmazonS3ClientBuilder.defaultClient();
13
AmazonDynamoDB dynamoDB = AmazonDynamoDBClientBuilder.defaultClient();
14
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();
15
```
16
17
### Pattern: Standard Builder
18
19
```java
20
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
21
import com.amazonaws.regions.Regions;
22
import com.amazonaws.ClientConfiguration;
23
24
// Recommended for production - explicit configuration
25
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
26
.withRegion(Regions.US_WEST_2)
27
.withCredentials(new DefaultAWSCredentialsProviderChain())
28
.withClientConfiguration(new ClientConfiguration()
29
.withMaxConnections(100)
30
.withConnectionTimeout(10000))
31
.build();
32
```
33
34
### Pattern: Custom Endpoint
35
36
```java
37
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
38
39
// For testing with LocalStack, DynamoDB Local, or custom endpoints
40
AmazonDynamoDB dynamoDB = AmazonDynamoDBClientBuilder.standard()
41
.withEndpointConfiguration(new EndpointConfiguration(
42
"http://localhost:8000",
43
"us-west-2"
44
))
45
.build();
46
```
47
48
## Credential Configuration Pattern
49
50
### Pattern: Default Credential Chain (Recommended)
51
52
```java
53
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
54
55
// Automatically searches in order:
56
// 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
57
// 2. System properties (aws.accessKeyId, aws.secretKey)
58
// 3. Web identity token
59
// 4. AWS credentials file (~/.aws/credentials)
60
// 5. ECS container credentials
61
// 6. EC2 instance profile credentials
62
63
DefaultAWSCredentialsProviderChain credentialsProvider =
64
new DefaultAWSCredentialsProviderChain();
65
66
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
67
.withCredentials(credentialsProvider)
68
.build();
69
```
70
71
### Pattern: Environment Variables
72
73
```bash
74
# Set credentials via environment variables
75
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
76
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
77
export AWS_REGION=us-west-2
78
```
79
80
```java
81
import com.amazonaws.auth.EnvironmentVariableCredentialsProvider;
82
83
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
84
.withCredentials(new EnvironmentVariableCredentialsProvider())
85
.build();
86
```
87
88
### Pattern: AWS Profile
89
90
```java
91
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
92
93
// Use specific profile from ~/.aws/credentials
94
ProfileCredentialsProvider credentialsProvider =
95
new ProfileCredentialsProvider("myprofile");
96
97
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
98
.withCredentials(credentialsProvider)
99
.build();
100
```
101
102
### Pattern: IAM Role (EC2, ECS, Lambda)
103
104
```java
105
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
106
107
// Automatically retrieves credentials from instance metadata
108
InstanceProfileCredentialsProvider credentialsProvider =
109
InstanceProfileCredentialsProvider.getInstance();
110
111
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
112
.withCredentials(credentialsProvider)
113
.build();
114
```
115
116
### Pattern: Assume Role (STS)
117
118
```java
119
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
120
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
121
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
122
123
AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.defaultClient();
124
125
STSAssumeRoleSessionCredentialsProvider credentialsProvider =
126
new STSAssumeRoleSessionCredentialsProvider.Builder(
127
"arn:aws:iam::123456789012:role/MyRole",
128
"session-name"
129
).withStsClient(stsClient).build();
130
131
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
132
.withCredentials(credentialsProvider)
133
.build();
134
```
135
136
## Request-Response Pattern
137
138
All service operations follow the request-response pattern.
139
140
### Pattern: Simple Request (Convenience Methods)
141
142
```java
143
// Many operations have convenience methods with common parameters
144
s3.putObject("my-bucket", "my-key", new File("/path/to/file"));
145
dynamoDB.getItem("MyTable", Map.of("id", new AttributeValue("123")));
146
```
147
148
### Pattern: Full Request Object
149
150
```java
151
import com.amazonaws.services.s3.model.*;
152
153
// Full control using request objects
154
PutObjectRequest request = new PutObjectRequest("my-bucket", "my-key", file)
155
.withCannedAcl(CannedAccessControlList.PublicRead)
156
.withStorageClass(StorageClass.StandardInfrequentAccess)
157
.withMetadata(metadata);
158
159
PutObjectResult result = s3.putObject(request);
160
String etag = result.getETag();
161
String versionId = result.getVersionId();
162
```
163
164
## Pagination Pattern
165
166
Most list operations return paginated results.
167
168
### Pattern: Manual Pagination with Continuation Token
169
170
```java
171
import com.amazonaws.services.s3.model.*;
172
173
// V2 API uses continuation token (RECOMMENDED)
174
ListObjectsV2Request request = new ListObjectsV2Request()
175
.withBucketName("my-bucket")
176
.withPrefix("photos/")
177
.withMaxKeys(1000);
178
179
ListObjectsV2Result result;
180
do {
181
result = s3.listObjectsV2(request);
182
183
// Process results
184
for (S3ObjectSummary summary : result.getObjectSummaries()) {
185
System.out.println(summary.getKey());
186
}
187
188
// Set continuation token for next page
189
request.setContinuationToken(result.getNextContinuationToken());
190
191
} while (result.isTruncated());
192
```
193
194
### Pattern: Pagination with Helper Method
195
196
```java
197
import com.amazonaws.services.s3.model.*;
198
199
// Some APIs provide helper methods for pagination
200
ObjectListing listing = s3.listObjects("my-bucket");
201
do {
202
for (S3ObjectSummary summary : listing.getObjectSummaries()) {
203
System.out.println(summary.getKey());
204
}
205
listing = s3.listNextBatchOfObjects(listing);
206
} while (listing.isTruncated());
207
```
208
209
### Pattern: DynamoDB Query Pagination
210
211
```java
212
import com.amazonaws.services.dynamodbv2.model.*;
213
214
QueryRequest queryRequest = new QueryRequest()
215
.withTableName("MyTable")
216
.withKeyConditionExpression("userId = :userId")
217
.withExpressionAttributeValues(
218
Map.of(":userId", new AttributeValue("123")))
219
.withLimit(100);
220
221
QueryResult result;
222
Map<String, AttributeValue> lastKey = null;
223
224
do {
225
if (lastKey != null) {
226
queryRequest.setExclusiveStartKey(lastKey);
227
}
228
229
result = dynamoDB.query(queryRequest);
230
231
// Process items
232
for (Map<String, AttributeValue> item : result.getItems()) {
233
// Process item
234
}
235
236
lastKey = result.getLastEvaluatedKey();
237
238
} while (lastKey != null);
239
```
240
241
## Asynchronous Operations Pattern
242
243
### Pattern: Async with Future
244
245
```java
246
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync;
247
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsyncClientBuilder;
248
import java.util.concurrent.Future;
249
250
// Create async client
251
AmazonDynamoDBAsync asyncClient = AmazonDynamoDBAsyncClientBuilder.standard()
252
.withRegion(Regions.US_EAST_1)
253
.build();
254
255
// Execute async operation
256
Future<PutItemResult> future = asyncClient.putItemAsync(putItemRequest);
257
258
// Continue with other work...
259
260
// Wait for result (blocking)
261
PutItemResult result = future.get();
262
```
263
264
### Pattern: Async with Callback
265
266
```java
267
import com.amazonaws.handlers.AsyncHandler;
268
269
asyncClient.putItemAsync(putItemRequest,
270
new AsyncHandler<PutItemRequest, PutItemResult>() {
271
@Override
272
public void onSuccess(PutItemRequest request, PutItemResult result) {
273
System.out.println("Put item succeeded");
274
// Handle success
275
}
276
277
@Override
278
public void onError(Exception exception) {
279
System.err.println("Put item failed: " + exception.getMessage());
280
// Handle error
281
}
282
});
283
284
// Non-blocking - continues immediately
285
```
286
287
## Exception Handling Pattern
288
289
### Pattern: Comprehensive Exception Handling
290
291
```java
292
import com.amazonaws.AmazonServiceException;
293
import com.amazonaws.SdkClientException;
294
295
try {
296
// Perform AWS operation
297
s3.getObject("my-bucket", "my-key");
298
299
} catch (AmazonServiceException ase) {
300
// Service-side error (4xx, 5xx HTTP status codes)
301
System.err.println("Service Error:");
302
System.err.println(" Error Code: " + ase.getErrorCode());
303
System.err.println(" Error Message: " + ase.getErrorMessage());
304
System.err.println(" HTTP Status: " + ase.getStatusCode());
305
System.err.println(" Request ID: " + ase.getRequestId());
306
System.err.println(" Service: " + ase.getServiceName());
307
308
// Handle specific error codes
309
switch (ase.getErrorCode()) {
310
case "NoSuchKey":
311
// Object doesn't exist
312
break;
313
case "AccessDenied":
314
// Permission denied
315
break;
316
case "NoSuchBucket":
317
// Bucket doesn't exist
318
break;
319
default:
320
// Other service errors
321
break;
322
}
323
324
} catch (SdkClientException sce) {
325
// Client-side error (network failure, parsing error, etc.)
326
System.err.println("Client Error: " + sce.getMessage());
327
328
if (sce.isRetryable()) {
329
// Error is retryable - implement retry logic
330
}
331
}
332
```
333
334
### Pattern: Service-Specific Exceptions
335
336
```java
337
import com.amazonaws.services.s3.model.AmazonS3Exception;
338
import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException;
339
340
try {
341
dynamoDB.describeTable("NonExistentTable");
342
} catch (ResourceNotFoundException e) {
343
System.err.println("Table does not exist");
344
} catch (AmazonServiceException e) {
345
System.err.println("Other DynamoDB error: " + e.getErrorCode());
346
}
347
348
try {
349
s3.getObject("my-bucket", "my-key");
350
} catch (AmazonS3Exception e) {
351
if (e.getStatusCode() == 404) {
352
System.err.println("Object not found");
353
}
354
}
355
```
356
357
## Waiter Pattern
358
359
Services with long-running operations provide waiters.
360
361
### Pattern: Basic Waiter
362
363
```java
364
import com.amazonaws.waiters.WaiterParameters;
365
import com.amazonaws.services.ec2.AmazonEC2;
366
import com.amazonaws.services.ec2.model.*;
367
368
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();
369
370
// Launch instance
371
RunInstancesRequest runRequest = new RunInstancesRequest()
372
.withImageId("ami-12345678")
373
.withInstanceType(InstanceType.T2Micro)
374
.withMinCount(1)
375
.withMaxCount(1);
376
377
RunInstancesResult runResult = ec2.runInstances(runRequest);
378
String instanceId = runResult.getReservation().getInstances().get(0).getInstanceId();
379
380
// Wait for instance to be running
381
try {
382
ec2.waiters().instanceRunning().run(
383
new WaiterParameters<>(
384
new DescribeInstancesRequest().withInstanceIds(instanceId)
385
)
386
);
387
System.out.println("Instance is running");
388
389
} catch (WaiterTimedOutException e) {
390
System.err.println("Timed out waiting for instance");
391
} catch (WaiterUnrecoverableException e) {
392
System.err.println("Instance entered unrecoverable state");
393
}
394
```
395
396
### Pattern: Waiter with Custom Polling
397
398
```java
399
import com.amazonaws.waiters.*;
400
401
PollingStrategy pollingStrategy = new PollingStrategy(
402
new MaxAttemptsRetryStrategy(40), // Max 40 attempts
403
new FixedDelayStrategy(15) // 15 seconds between attempts
404
);
405
406
ec2.waiters().instanceRunning().run(
407
new WaiterParameters<>(request)
408
.withPollingStrategy(pollingStrategy)
409
);
410
```
411
412
## Batch Operations Pattern
413
414
### Pattern: S3 Batch Delete
415
416
```java
417
import com.amazonaws.services.s3.model.*;
418
import java.util.ArrayList;
419
import java.util.List;
420
421
// Delete up to 1000 objects at once
422
DeleteObjectsRequest multiDeleteRequest = new DeleteObjectsRequest("my-bucket");
423
424
List<KeyVersion> keys = new ArrayList<>();
425
keys.add(new KeyVersion("file1.txt"));
426
keys.add(new KeyVersion("file2.txt"));
427
keys.add(new KeyVersion("file3.txt", "version-id")); // Specific version
428
429
multiDeleteRequest.setKeys(keys);
430
431
DeleteObjectsResult result = s3.deleteObjects(multiDeleteRequest);
432
433
// Check results
434
System.out.println("Deleted: " + result.getDeletedObjects().size());
435
for (DeleteError error : result.getErrors()) {
436
System.err.println("Failed to delete: " + error.getKey() +
437
" - " + error.getMessage());
438
}
439
```
440
441
### Pattern: DynamoDB Batch Write
442
443
```java
444
import com.amazonaws.services.dynamodbv2.model.*;
445
import java.util.*;
446
447
// Batch write up to 25 items
448
Map<String, List<WriteRequest>> requestItems = new HashMap<>();
449
List<WriteRequest> writes = new ArrayList<>();
450
451
// Add put requests
452
Map<String, AttributeValue> item1 = new HashMap<>();
453
item1.put("id", new AttributeValue("1"));
454
item1.put("name", new AttributeValue("Item 1"));
455
writes.add(new WriteRequest().withPutRequest(new PutRequest().withItem(item1)));
456
457
// Add delete requests
458
Map<String, AttributeValue> key2 = new HashMap<>();
459
key2.put("id", new AttributeValue("2"));
460
writes.add(new WriteRequest().withDeleteRequest(new DeleteRequest().withKey(key2)));
461
462
requestItems.put("MyTable", writes);
463
464
BatchWriteItemResult result = dynamoDB.batchWriteItem(requestItems);
465
466
// Handle unprocessed items
467
Map<String, List<WriteRequest>> unprocessed = result.getUnprocessedItems();
468
if (unprocessed != null && !unprocessed.isEmpty()) {
469
// Retry unprocessed items with exponential backoff
470
int retries = 0;
471
while (!unprocessed.isEmpty() && retries < 5) {
472
Thread.sleep((long) Math.pow(2, retries) * 1000); // Exponential backoff
473
result = dynamoDB.batchWriteItem(unprocessed);
474
unprocessed = result.getUnprocessedItems();
475
retries++;
476
}
477
}
478
```
479
480
### Pattern: DynamoDB Batch Get
481
482
```java
483
import com.amazonaws.services.dynamodbv2.model.*;
484
485
// Batch get up to 100 items
486
Map<String, KeysAndAttributes> requestItems = new HashMap<>();
487
488
List<Map<String, AttributeValue>> keys = new ArrayList<>();
489
keys.add(Map.of("id", new AttributeValue("1")));
490
keys.add(Map.of("id", new AttributeValue("2")));
491
keys.add(Map.of("id", new AttributeValue("3")));
492
493
KeysAndAttributes keysAndAttributes = new KeysAndAttributes()
494
.withKeys(keys)
495
.withConsistentRead(true);
496
497
requestItems.put("MyTable", keysAndAttributes);
498
499
BatchGetItemResult result = dynamoDB.batchGetItem(requestItems);
500
501
// Process results
502
List<Map<String, AttributeValue>> items = result.getResponses().get("MyTable");
503
for (Map<String, AttributeValue> item : items) {
504
System.out.println("Item: " + item.get("id").getS());
505
}
506
507
// Handle unprocessed keys
508
Map<String, KeysAndAttributes> unprocessed = result.getUnprocessedKeys();
509
if (unprocessed != null && !unprocessed.isEmpty()) {
510
// Retry unprocessed keys
511
result = dynamoDB.batchGetItem(unprocessed);
512
}
513
```
514
515
## Retry Pattern
516
517
### Pattern: Automatic Retries (Default)
518
519
```java
520
import com.amazonaws.ClientConfiguration;
521
import com.amazonaws.retry.PredefinedRetryPolicies;
522
523
// Default retry policy automatically retries transient errors
524
ClientConfiguration config = new ClientConfiguration()
525
.withRetryPolicy(PredefinedRetryPolicies.DEFAULT)
526
.withMaxErrorRetry(3);
527
528
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
529
.withClientConfiguration(config)
530
.build();
531
532
// Automatic retries for:
533
// - Network failures
534
// - 500 Internal Server Error
535
// - 503 Service Unavailable
536
// - Throttling errors
537
```
538
539
### Pattern: Custom Retry Logic
540
541
```java
542
import com.amazonaws.AmazonServiceException;
543
544
int maxRetries = 3;
545
int retryCount = 0;
546
boolean success = false;
547
548
while (!success && retryCount < maxRetries) {
549
try {
550
dynamoDB.putItem(putItemRequest);
551
success = true;
552
553
} catch (AmazonServiceException e) {
554
if (e.getErrorCode().equals("ProvisionedThroughputExceededException")) {
555
retryCount++;
556
if (retryCount < maxRetries) {
557
// Exponential backoff
558
Thread.sleep((long) Math.pow(2, retryCount) * 1000);
559
} else {
560
throw e;
561
}
562
} else {
563
throw e; // Don't retry other errors
564
}
565
}
566
}
567
```
568
569
## Resource Management Pattern
570
571
### Pattern: Client Lifecycle
572
573
```java
574
// Clients are thread-safe and expensive to create - reuse them
575
public class MyService {
576
private final AmazonS3 s3;
577
private final AmazonDynamoDB dynamoDB;
578
579
public MyService() {
580
// Create clients once
581
this.s3 = AmazonS3ClientBuilder.defaultClient();
582
this.dynamoDB = AmazonDynamoDBClientBuilder.defaultClient();
583
}
584
585
public void doWork() {
586
// Reuse clients across multiple operations
587
s3.listBuckets();
588
dynamoDB.listTables();
589
}
590
591
// Optional: Shutdown when application terminates
592
public void shutdown() {
593
s3.shutdown();
594
dynamoDB.shutdown();
595
}
596
}
597
```
598
599
### Pattern: Try-with-Resources
600
601
```java
602
import com.amazonaws.services.s3.model.S3Object;
603
import com.amazonaws.services.s3.model.S3ObjectInputStream;
604
import java.io.BufferedReader;
605
import java.io.InputStreamReader;
606
607
// Always close S3Object input streams
608
try (S3Object object = s3.getObject("my-bucket", "my-key");
609
S3ObjectInputStream content = object.getObjectContent();
610
BufferedReader reader = new BufferedReader(new InputStreamReader(content))) {
611
612
String line;
613
while ((line = reader.readLine()) != null) {
614
System.out.println(line);
615
}
616
} // Streams are automatically closed
617
```
618
619
## Testing Pattern
620
621
### Pattern: Local Testing with Endpoint Override
622
623
```java
624
import org.junit.Before;
625
import org.junit.Test;
626
627
public class DynamoDBTest {
628
private AmazonDynamoDB dynamoDB;
629
630
@Before
631
public void setUp() {
632
// Point to DynamoDB Local for testing
633
dynamoDB = AmazonDynamoDBClientBuilder.standard()
634
.withEndpointConfiguration(
635
new EndpointConfiguration(
636
"http://localhost:8000",
637
"us-west-2"
638
))
639
.withCredentials(new AWSStaticCredentialsProvider(
640
new BasicAWSCredentials("dummy", "dummy")))
641
.build();
642
}
643
644
@Test
645
public void testPutItem() {
646
// Test against local DynamoDB
647
}
648
}
649
```
650
651
## Configuration Best Practices
652
653
### Pattern: Production-Ready Configuration
654
655
```java
656
import com.amazonaws.ClientConfiguration;
657
import com.amazonaws.retry.PredefinedRetryPolicies;
658
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
659
import com.amazonaws.regions.DefaultAwsRegionProviderChain;
660
661
// Production-ready client configuration
662
ClientConfiguration config = new ClientConfiguration()
663
// Connection pooling
664
.withMaxConnections(100)
665
.withConnectionTimeout(10000) // 10 seconds
666
.withSocketTimeout(50000) // 50 seconds
667
.withConnectionTTL(60000) // 60 seconds
668
669
// Timeouts
670
.withRequestTimeout(0) // Disabled (use socket timeout)
671
.withClientExecutionTimeout(0) // Disabled
672
673
// Retries
674
.withRetryPolicy(PredefinedRetryPolicies.DEFAULT)
675
.withMaxErrorRetry(3)
676
677
// User agent
678
.withUserAgentPrefix("MyApp/1.0");
679
680
// Build client with best practices
681
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
682
.withRegion(new DefaultAwsRegionProviderChain().getRegion())
683
.withCredentials(new DefaultAWSCredentialsProviderChain())
684
.withClientConfiguration(config)
685
.build();
686
```
687
688
## Summary
689
690
These patterns are consistent across all 371+ AWS services in the SDK. Once you understand these core patterns, you can work with any AWS service efficiently. Key principles:
691
692
1. **Builder Pattern**: Use fluent builders for client creation
693
2. **Credential Chain**: Use DefaultAWSCredentialsProviderChain for flexibility
694
3. **Request/Response**: Every operation uses typed request and result objects
695
4. **Pagination**: Always handle paginated results properly
696
5. **Exception Handling**: Catch AmazonServiceException and SdkClientException
697
6. **Resource Management**: Reuse clients, close streams
698
7. **Batch Operations**: Use batch APIs to reduce API calls
699
8. **Waiters**: Use built-in waiters for polling instead of manual polling
700