0
# Waiters & Polling
1
2
The AWS Java SDK Core provides a comprehensive waiter framework for polling-based state transition waiting, enabling applications to wait for resources to reach desired states with configurable polling strategies and timeout handling.
3
4
## Core Waiter Framework
5
6
### Waiter Interface
7
8
```java { .api }
9
// Core waiter interface
10
interface Waiter<Input> {
11
// Synchronous waiting with default handler
12
void run(Input input) throws WaiterUnrecoverableException, WaiterTimedOutException;
13
14
// Synchronous waiting with custom handler
15
void run(Input input, WaiterHandler<Input> waiterHandler)
16
throws WaiterUnrecoverableException, WaiterTimedOutException;
17
}
18
19
// Default waiter implementation
20
class WaiterImpl<Input, Output> implements Waiter<Input> {
21
public WaiterImpl(WaiterExecutionBuilder<Input, Output> waiterExecutionBuilder);
22
23
public void run(Input input) throws WaiterUnrecoverableException, WaiterTimedOutException;
24
public void run(Input input, WaiterHandler<Input> waiterHandler)
25
throws WaiterUnrecoverableException, WaiterTimedOutException;
26
}
27
28
// Waiter builder for creating waiters
29
class WaiterBuilder<Input, Output> {
30
public static <Input, Output> WaiterBuilder<Input, Output> newBuilder();
31
32
// Configuration methods
33
public WaiterBuilder<Input, Output> withSdkFunction(SdkFunction<Input, Output> sdkFunction);
34
public WaiterBuilder<Input, Output> withAcceptors(WaiterAcceptor<Output>... acceptors);
35
public WaiterBuilder<Input, Output> withAcceptors(List<WaiterAcceptor<Output>> acceptors);
36
public WaiterBuilder<Input, Output> withDefaultPollingStrategy(PollingStrategy pollingStrategy);
37
public WaiterBuilder<Input, Output> withExecutorService(ExecutorService executorService);
38
39
// Build the waiter
40
public Waiter<Input> build();
41
}
42
```
43
44
### Waiter Execution
45
46
```java { .api }
47
// Waiter execution context
48
class WaiterExecution<Input, Output> {
49
public WaiterExecution(WaiterExecutionBuilder<Input, Output> waiterExecutionBuilder);
50
51
public WaiterState execute(Input input) throws Exception;
52
public WaiterState execute(Input input, WaiterHandler<Input> waiterHandler) throws Exception;
53
}
54
55
// Builder for waiter execution
56
class WaiterExecutionBuilder<Input, Output> {
57
public static <Input, Output> WaiterExecutionBuilder<Input, Output> builder();
58
59
// Configuration methods
60
public WaiterExecutionBuilder<Input, Output> sdkFunction(SdkFunction<Input, Output> sdkFunction);
61
public WaiterExecutionBuilder<Input, Output> acceptors(List<WaiterAcceptor<Output>> acceptors);
62
public WaiterExecutionBuilder<Input, Output> pollingStrategy(PollingStrategy pollingStrategy);
63
public WaiterExecutionBuilder<Input, Output> executorService(ExecutorService executorService);
64
65
// Build execution context
66
public WaiterExecution<Input, Output> build();
67
}
68
```
69
70
## Polling Strategies
71
72
### Core Polling Strategy
73
74
```java { .api }
75
// Strategy for polling operations
76
class PollingStrategy {
77
public PollingStrategy(RetryStrategy retryStrategy, DelayStrategy delayStrategy);
78
79
public RetryStrategy getRetryStrategy();
80
public DelayStrategy getDelayStrategy();
81
82
// Retry strategy interface
83
interface RetryStrategy {
84
boolean shouldRetry(PollingStrategyContext pollingStrategyContext);
85
}
86
87
// Delay strategy interface
88
interface DelayStrategy {
89
void delay(PollingStrategyContext pollingStrategyContext) throws InterruptedException;
90
}
91
}
92
93
// Context for polling strategies
94
class PollingStrategyContext {
95
public static PollingStrategyContext of(int retriesAttempted, WaiterState previousWaiterState);
96
97
public int getRetriesAttempted();
98
public WaiterState getPreviousWaiterState();
99
public PollingStrategyContext withRetriesAttempted(int retriesAttempted);
100
public PollingStrategyContext withPreviousWaiterState(WaiterState previousWaiterState);
101
}
102
```
103
104
### Built-in Strategies
105
106
```java { .api }
107
// Maximum attempts retry strategy
108
class MaxAttemptsRetryStrategy implements PollingStrategy.RetryStrategy {
109
public MaxAttemptsRetryStrategy(int maxAttempts);
110
111
public boolean shouldRetry(PollingStrategyContext pollingStrategyContext);
112
public int getMaxAttempts();
113
}
114
115
// Fixed delay strategy
116
class FixedDelayStrategy implements PollingStrategy.DelayStrategy {
117
public FixedDelayStrategy(int delayInSeconds);
118
119
public void delay(PollingStrategyContext pollingStrategyContext) throws InterruptedException;
120
public int getDelayInSeconds();
121
}
122
```
123
124
## Waiter States and Exceptions
125
126
### Waiter States
127
128
```java { .api }
129
// Waiter state enumeration
130
enum WaiterState {
131
SUCCESS, // Desired state reached
132
RETRY, // Continue polling
133
FAILURE; // Unrecoverable failure
134
135
public boolean isSuccess();
136
public boolean isFailure();
137
public boolean isRetry();
138
}
139
```
140
141
### Waiter Exceptions
142
143
```java { .api }
144
// Exception for waiter timeouts
145
class WaiterTimedOutException extends Exception {
146
public WaiterTimedOutException(String message);
147
public WaiterTimedOutException(String message, Throwable cause);
148
}
149
150
// Exception for unrecoverable waiter errors
151
class WaiterUnrecoverableException extends Exception {
152
public WaiterUnrecoverableException(String message);
153
public WaiterUnrecoverableException(String message, Throwable cause);
154
}
155
```
156
157
## Waiter Acceptors
158
159
### Core Acceptor Framework
160
161
```java { .api }
162
// Base class for waiter acceptors
163
abstract class WaiterAcceptor<Output> {
164
public abstract WaiterState matches(Output output);
165
public abstract WaiterState getState();
166
167
// Create acceptor instances
168
public static <Output> WaiterAcceptor<Output> successOn(Predicate<Output> predicate);
169
public static <Output> WaiterAcceptor<Output> retryOn(Predicate<Output> predicate);
170
public static <Output> WaiterAcceptor<Output> failureOn(Predicate<Output> predicate);
171
}
172
173
// HTTP success status acceptor
174
class HttpSuccessStatusAcceptor<Output> extends WaiterAcceptor<Output> {
175
public HttpSuccessStatusAcceptor();
176
177
public WaiterState matches(Output output);
178
public WaiterState getState();
179
}
180
181
// HTTP failure status acceptor
182
class HttpFailureStatusAcceptor<Output> extends WaiterAcceptor<Output> {
183
public HttpFailureStatusAcceptor();
184
185
public WaiterState matches(Output output);
186
public WaiterState getState();
187
}
188
189
// Path matcher for acceptors
190
interface AcceptorPathMatcher {
191
boolean matches(Object objectToMatch);
192
193
// Create path matchers
194
static AcceptorPathMatcher pathAll(AcceptorPathMatcher... matchers);
195
static AcceptorPathMatcher pathAny(AcceptorPathMatcher... matchers);
196
static AcceptorPathMatcher path(String path, Object expectedValue);
197
static AcceptorPathMatcher path(String path, AcceptorPathMatcher nestedMatcher);
198
}
199
```
200
201
## Waiter Utilities
202
203
### SDK Function Interface
204
205
```java { .api }
206
// Function interface for SDK operations
207
interface SdkFunction<Input, Output> {
208
Output apply(Input input) throws Exception;
209
}
210
```
211
212
### Waiter Handler
213
214
```java { .api }
215
// Handler for waiter events
216
interface WaiterHandler<Input> {
217
void onWaiterSuccess(Input input);
218
void onWaiterFailure(Input input, Throwable throwable);
219
void beforeWaiterExecution(Input input);
220
void beforePolling(Input input, int pollAttempt);
221
}
222
223
// No-operation waiter handler
224
class NoOpWaiterHandler<Input> implements WaiterHandler<Input> {
225
public void onWaiterSuccess(Input input) {}
226
public void onWaiterFailure(Input input, Throwable throwable) {}
227
public void beforeWaiterExecution(Input input) {}
228
public void beforePolling(Input input, int pollAttempt) {}
229
}
230
```
231
232
### Waiter Executor Factory
233
234
```java { .api }
235
// Factory for waiter executors
236
class WaiterExecutorServiceFactory {
237
public static ExecutorService buildExecutorServiceForWaiter();
238
public static ExecutorService buildExecutorServiceForWaiter(String threadNamePrefix);
239
public static ScheduledExecutorService buildScheduledExecutorForWaiter();
240
public static ScheduledExecutorService buildScheduledExecutorForWaiter(String threadNamePrefix);
241
}
242
```
243
244
## Usage Examples
245
246
### Basic Waiter Creation
247
248
```java
249
import com.amazonaws.waiters.*;
250
import java.util.concurrent.ExecutorService;
251
252
// Create SDK function for the operation to poll
253
SdkFunction<MyRequest, MyResponse> sdkFunction = new SdkFunction<MyRequest, MyResponse>() {
254
@Override
255
public MyResponse apply(MyRequest request) throws Exception {
256
// Call your AWS service client method
257
return myServiceClient.describeResource(request);
258
}
259
};
260
261
// Create acceptors to define success/failure conditions
262
WaiterAcceptor<MyResponse> successAcceptor = WaiterAcceptor.successOn(response ->
263
"ACTIVE".equals(response.getStatus())
264
);
265
266
WaiterAcceptor<MyResponse> failureAcceptor = WaiterAcceptor.failureOn(response ->
267
"FAILED".equals(response.getStatus())
268
);
269
270
WaiterAcceptor<MyResponse> retryAcceptor = WaiterAcceptor.retryOn(response ->
271
"PENDING".equals(response.getStatus())
272
);
273
274
// Create polling strategy (max 30 attempts, 10 second intervals)
275
PollingStrategy pollingStrategy = new PollingStrategy(
276
new MaxAttemptsRetryStrategy(30),
277
new FixedDelayStrategy(10)
278
);
279
280
// Build the waiter
281
Waiter<MyRequest> waiter = WaiterBuilder.<MyRequest, MyResponse>newBuilder()
282
.withSdkFunction(sdkFunction)
283
.withAcceptors(successAcceptor, failureAcceptor, retryAcceptor)
284
.withDefaultPollingStrategy(pollingStrategy)
285
.build();
286
```
287
288
### Using the Waiter
289
290
```java
291
import com.amazonaws.waiters.*;
292
293
MyRequest request = new MyRequest().withResourceId("resource-123");
294
295
try {
296
// Wait with default handler
297
waiter.run(request);
298
System.out.println("Resource became active!");
299
300
} catch (WaiterTimedOutException e) {
301
System.err.println("Timed out waiting for resource to become active");
302
} catch (WaiterUnrecoverableException e) {
303
System.err.println("Unrecoverable error: " + e.getMessage());
304
}
305
```
306
307
### Custom Waiter Handler
308
309
```java
310
import com.amazonaws.waiters.*;
311
312
// Create custom waiter handler
313
WaiterHandler<MyRequest> customHandler = new WaiterHandler<MyRequest>() {
314
@Override
315
public void onWaiterSuccess(MyRequest input) {
316
System.out.println("Success! Resource " + input.getResourceId() + " is ready");
317
}
318
319
@Override
320
public void onWaiterFailure(MyRequest input, Throwable throwable) {
321
System.err.println("Failed waiting for " + input.getResourceId() + ": " +
322
throwable.getMessage());
323
}
324
325
@Override
326
public void beforeWaiterExecution(MyRequest input) {
327
System.out.println("Starting to wait for resource " + input.getResourceId());
328
}
329
330
@Override
331
public void beforePolling(MyRequest input, int pollAttempt) {
332
System.out.println("Poll attempt " + pollAttempt + " for resource " +
333
input.getResourceId());
334
}
335
};
336
337
// Use custom handler
338
try {
339
waiter.run(request, customHandler);
340
} catch (Exception e) {
341
System.err.println("Waiter failed: " + e.getMessage());
342
}
343
```
344
345
### Advanced Polling Strategy
346
347
```java
348
import com.amazonaws.waiters.*;
349
import java.util.concurrent.TimeUnit;
350
351
// Custom retry strategy with time-based limits
352
PollingStrategy.RetryStrategy timeBasedRetry = new PollingStrategy.RetryStrategy() {
353
private final long startTime = System.currentTimeMillis();
354
private final long maxWaitTimeMs = TimeUnit.MINUTES.toMillis(10); // 10 minutes max
355
356
@Override
357
public boolean shouldRetry(PollingStrategyContext context) {
358
long elapsedTime = System.currentTimeMillis() - startTime;
359
return elapsedTime < maxWaitTimeMs &&
360
context.getRetriesAttempted() < 100 &&
361
context.getPreviousWaiterState() == WaiterState.RETRY;
362
}
363
};
364
365
// Custom delay strategy with exponential backoff
366
PollingStrategy.DelayStrategy exponentialDelay = new PollingStrategy.DelayStrategy() {
367
@Override
368
public void delay(PollingStrategyContext context) throws InterruptedException {
369
int attempts = context.getRetriesAttempted();
370
long delayMs = Math.min(1000 * (1L << attempts), 30000); // Cap at 30 seconds
371
372
System.out.println("Waiting " + delayMs + "ms before next poll attempt");
373
Thread.sleep(delayMs);
374
}
375
};
376
377
// Create advanced polling strategy
378
PollingStrategy advancedStrategy = new PollingStrategy(timeBasedRetry, exponentialDelay);
379
380
// Use in waiter
381
Waiter<MyRequest> advancedWaiter = WaiterBuilder.<MyRequest, MyResponse>newBuilder()
382
.withSdkFunction(sdkFunction)
383
.withAcceptors(successAcceptor, failureAcceptor, retryAcceptor)
384
.withDefaultPollingStrategy(advancedStrategy)
385
.build();
386
```
387
388
### HTTP Status Code Acceptors
389
390
```java
391
import com.amazonaws.waiters.*;
392
import com.amazonaws.http.HttpResponse;
393
394
// For operations that return HTTP responses
395
SdkFunction<MyRequest, HttpResponse> httpFunction = request -> {
396
// Make HTTP request and return response
397
return httpClient.execute(request);
398
};
399
400
// HTTP success acceptor (2xx status codes)
401
WaiterAcceptor<HttpResponse> httpSuccess = new HttpSuccessStatusAcceptor<>();
402
403
// HTTP failure acceptor (4xx/5xx status codes)
404
WaiterAcceptor<HttpResponse> httpFailure = new HttpFailureStatusAcceptor<>();
405
406
// Custom status code acceptor
407
WaiterAcceptor<HttpResponse> customStatusAcceptor = WaiterAcceptor.successOn(response -> {
408
int statusCode = response.getStatusCode();
409
return statusCode == 200 || statusCode == 202; // Success on 200 or 202
410
});
411
412
Waiter<MyRequest> httpWaiter = WaiterBuilder.<MyRequest, HttpResponse>newBuilder()
413
.withSdkFunction(httpFunction)
414
.withAcceptors(httpSuccess, httpFailure, customStatusAcceptor)
415
.withDefaultPollingStrategy(pollingStrategy)
416
.build();
417
```
418
419
### Path-Based Acceptors
420
421
```java
422
import com.amazonaws.waiters.*;
423
424
// Acceptor using path matching for complex response structures
425
WaiterAcceptor<MyResponse> pathBasedAcceptor = new WaiterAcceptor<MyResponse>() {
426
@Override
427
public WaiterState matches(MyResponse output) {
428
// Check nested field values using path matching
429
if (AcceptorPathMatcher.path("resource.state", "ACTIVE").matches(output)) {
430
return WaiterState.SUCCESS;
431
} else if (AcceptorPathMatcher.path("resource.state", "FAILED").matches(output)) {
432
return WaiterState.FAILURE;
433
}
434
return WaiterState.RETRY;
435
}
436
437
@Override
438
public WaiterState getState() {
439
return WaiterState.SUCCESS; // This acceptor can return SUCCESS or FAILURE
440
}
441
};
442
443
// Multiple condition acceptor
444
WaiterAcceptor<MyResponse> multiConditionAcceptor = new WaiterAcceptor<MyResponse>() {
445
@Override
446
public WaiterState matches(MyResponse output) {
447
// All conditions must match
448
AcceptorPathMatcher allMatch = AcceptorPathMatcher.pathAll(
449
AcceptorPathMatcher.path("resource.state", "READY"),
450
AcceptorPathMatcher.path("resource.healthy", true),
451
AcceptorPathMatcher.path("resource.endpoints", AcceptorPathMatcher.pathAny(
452
AcceptorPathMatcher.path("[0].status", "UP"),
453
AcceptorPathMatcher.path("[1].status", "UP")
454
))
455
);
456
457
return allMatch.matches(output) ? WaiterState.SUCCESS : WaiterState.RETRY;
458
}
459
460
@Override
461
public WaiterState getState() {
462
return WaiterState.SUCCESS;
463
}
464
};
465
```
466
467
### Async Waiter with Custom Executor
468
469
```java
470
import com.amazonaws.waiters.*;
471
import java.util.concurrent.*;
472
473
// Create custom executor service
474
ExecutorService customExecutor = Executors.newFixedThreadPool(5, r -> {
475
Thread t = new Thread(r, "custom-waiter-thread");
476
t.setDaemon(true);
477
return t;
478
});
479
480
// Build waiter with custom executor
481
Waiter<MyRequest> asyncWaiter = WaiterBuilder.<MyRequest, MyResponse>newBuilder()
482
.withSdkFunction(sdkFunction)
483
.withAcceptors(successAcceptor, failureAcceptor, retryAcceptor)
484
.withDefaultPollingStrategy(pollingStrategy)
485
.withExecutorService(customExecutor)
486
.build();
487
488
// Use waiter asynchronously
489
CompletableFuture<Void> waitFuture = CompletableFuture.runAsync(() -> {
490
try {
491
asyncWaiter.run(request);
492
System.out.println("Async wait completed successfully");
493
} catch (Exception e) {
494
System.err.println("Async wait failed: " + e.getMessage());
495
throw new RuntimeException(e);
496
}
497
}, customExecutor);
498
499
// Handle completion
500
waitFuture.whenComplete((result, throwable) -> {
501
if (throwable != null) {
502
System.err.println("Waiter failed: " + throwable.getMessage());
503
} else {
504
System.out.println("Waiter completed successfully");
505
}
506
});
507
508
// Don't forget to shutdown executor when done
509
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
510
customExecutor.shutdown();
511
try {
512
if (!customExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
513
customExecutor.shutdownNow();
514
}
515
} catch (InterruptedException e) {
516
customExecutor.shutdownNow();
517
}
518
}));
519
```
520
521
### Waiter Execution with Direct Control
522
523
```java
524
import com.amazonaws.waiters.*;
525
526
// Create waiter execution for direct control
527
WaiterExecution<MyRequest, MyResponse> execution =
528
WaiterExecutionBuilder.<MyRequest, MyResponse>builder()
529
.sdkFunction(sdkFunction)
530
.acceptors(Arrays.asList(successAcceptor, failureAcceptor, retryAcceptor))
531
.pollingStrategy(pollingStrategy)
532
.build();
533
534
// Manual execution loop
535
MyRequest request = new MyRequest().withResourceId("resource-123");
536
int attempts = 0;
537
WaiterState state = WaiterState.RETRY;
538
539
while (state == WaiterState.RETRY && attempts < 30) {
540
try {
541
System.out.println("Attempt " + (attempts + 1));
542
state = execution.execute(request);
543
544
if (state == WaiterState.SUCCESS) {
545
System.out.println("Success after " + (attempts + 1) + " attempts");
546
break;
547
} else if (state == WaiterState.FAILURE) {
548
System.err.println("Failure after " + (attempts + 1) + " attempts");
549
break;
550
}
551
552
attempts++;
553
554
// Manual delay between attempts
555
if (state == WaiterState.RETRY && attempts < 30) {
556
Thread.sleep(5000); // 5 second delay
557
}
558
559
} catch (Exception e) {
560
System.err.println("Exception during wait: " + e.getMessage());
561
break;
562
}
563
}
564
565
if (state == WaiterState.RETRY) {
566
System.err.println("Timed out after " + attempts + " attempts");
567
}
568
```
569
570
## Best Practices
571
572
1. **Appropriate Timeouts**: Set reasonable maximum retry counts and total wait times to prevent infinite waiting.
573
574
2. **Exponential Backoff**: Use exponential backoff with jitter for better resource utilization and to avoid overwhelming services.
575
576
3. **Error Handling**: Implement proper error handling for both timeout and unrecoverable failure scenarios.
577
578
4. **Resource Management**: Properly shutdown custom executor services to prevent resource leaks.
579
580
5. **Logging and Monitoring**: Implement comprehensive logging to track waiter progress and diagnose issues.
581
582
6. **State Validation**: Carefully design acceptor logic to correctly identify success, failure, and retry states.
583
584
7. **Performance Considerations**: Balance polling frequency with resource consumption and service load.
585
586
8. **Async Operations**: Use async waiters for long-running operations to avoid blocking application threads.
587
588
9. **Custom Handlers**: Implement custom waiter handlers for better observability and debugging.
589
590
10. **Graceful Degradation**: Have fallback strategies when waiters timeout or fail.
591
592
The waiter framework provides comprehensive polling capabilities that enable applications to efficiently wait for AWS resources to reach desired states while maintaining proper resource management and error handling.