or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arn-support.mdauthentication.mdclient-builders.mdendpoint-discovery.mdexception-handling.mdhttp-transport.mdindex.mdmetrics-monitoring.mdprotocols.mdregions-endpoints.mdretry-policies.mdutilities.mdwaiters.md

waiters.mddocs/

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.