1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.codegen.poet.waiters; 17 18 import static javax.lang.model.element.Modifier.FINAL; 19 import static javax.lang.model.element.Modifier.PRIVATE; 20 import static javax.lang.model.element.Modifier.PUBLIC; 21 import static javax.lang.model.element.Modifier.STATIC; 22 import static software.amazon.awssdk.utils.internal.CodegenNamingUtils.lowercaseFirstChar; 23 24 import com.fasterxml.jackson.jr.stree.JrsString; 25 import com.squareup.javapoet.ClassName; 26 import com.squareup.javapoet.CodeBlock; 27 import com.squareup.javapoet.FieldSpec; 28 import com.squareup.javapoet.MethodSpec; 29 import com.squareup.javapoet.ParameterizedTypeName; 30 import com.squareup.javapoet.TypeName; 31 import com.squareup.javapoet.TypeSpec; 32 import com.squareup.javapoet.TypeVariableName; 33 import com.squareup.javapoet.WildcardTypeName; 34 import java.time.Duration; 35 import java.util.ArrayList; 36 import java.util.Comparator; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Objects; 40 import java.util.Optional; 41 import java.util.function.Consumer; 42 import java.util.stream.Collectors; 43 import java.util.stream.Stream; 44 import javax.lang.model.element.Modifier; 45 import software.amazon.awssdk.annotations.SdkInternalApi; 46 import software.amazon.awssdk.annotations.ThreadSafe; 47 import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; 48 import software.amazon.awssdk.awscore.exception.AwsServiceException; 49 import software.amazon.awssdk.codegen.emitters.tasks.WaitersRuntimeGeneratorTask; 50 import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel; 51 import software.amazon.awssdk.codegen.model.intermediate.OperationModel; 52 import software.amazon.awssdk.codegen.model.service.Acceptor; 53 import software.amazon.awssdk.codegen.model.service.WaiterDefinition; 54 import software.amazon.awssdk.codegen.poet.ClassSpec; 55 import software.amazon.awssdk.codegen.poet.PoetExtension; 56 import software.amazon.awssdk.codegen.poet.PoetUtils; 57 import software.amazon.awssdk.core.ApiName; 58 import software.amazon.awssdk.core.internal.waiters.WaiterAttribute; 59 import software.amazon.awssdk.core.retry.backoff.BackoffStrategy; 60 import software.amazon.awssdk.core.retry.backoff.FixedDelayBackoffStrategy; 61 import software.amazon.awssdk.core.waiters.WaiterAcceptor; 62 import software.amazon.awssdk.core.waiters.WaiterOverrideConfiguration; 63 import software.amazon.awssdk.core.waiters.WaiterState; 64 import software.amazon.awssdk.utils.AttributeMap; 65 import software.amazon.awssdk.utils.SdkAutoCloseable; 66 67 /** 68 * Base class containing common logic shared between the sync waiter class and the async waiter class 69 */ 70 public abstract class BaseWaiterClassSpec implements ClassSpec { 71 72 private static final String WAITERS_USER_AGENT = "waiter"; 73 private final IntermediateModel model; 74 private final String modelPackage; 75 private final Map<String, WaiterDefinition> waiters; 76 private final ClassName waiterClassName; 77 private final JmesPathAcceptorGenerator jmesPathAcceptorGenerator; 78 private final PoetExtension poetExtensions; 79 BaseWaiterClassSpec(IntermediateModel model, ClassName waiterClassName)80 public BaseWaiterClassSpec(IntermediateModel model, ClassName waiterClassName) { 81 this.model = model; 82 this.modelPackage = model.getMetadata().getFullModelPackageName(); 83 this.waiters = model.getWaiters(); 84 this.waiterClassName = waiterClassName; 85 this.jmesPathAcceptorGenerator = new JmesPathAcceptorGenerator(waitersRuntimeClass()); 86 this.poetExtensions = new PoetExtension(model); 87 } 88 89 @Override poetSpec()90 public TypeSpec poetSpec() { 91 TypeSpec.Builder typeSpecBuilder = PoetUtils.createClassBuilder(className()); 92 typeSpecBuilder.addAnnotation(SdkInternalApi.class); 93 typeSpecBuilder.addAnnotation(ThreadSafe.class); 94 typeSpecBuilder.addModifiers(FINAL); 95 typeSpecBuilder.addSuperinterface(interfaceClassName()); 96 typeSpecBuilder.addMethod(constructor()); 97 typeSpecBuilder.addField(FieldSpec.builder(ParameterizedTypeName.get(WaiterAttribute.class, SdkAutoCloseable.class), 98 "CLIENT_ATTRIBUTE", PRIVATE, STATIC, FINAL) 99 .initializer("new $T<>($T.class)", WaiterAttribute.class, SdkAutoCloseable.class) 100 .build()); 101 typeSpecBuilder.addField(clientClassName(), "client", PRIVATE, FINAL); 102 typeSpecBuilder.addField(ClassName.get(AttributeMap.class), "managedResources", PRIVATE, FINAL); 103 typeSpecBuilder.addMethod(staticErrorCodeMethod()); 104 typeSpecBuilder.addMethods(waiterOperations()); 105 typeSpecBuilder.addMethods(waiterAcceptorInitializers()); 106 typeSpecBuilder.addMethods(waiterConfigInitializers()); 107 typeSpecBuilder.addFields(waitersFields()); 108 additionalTypeSpecModification(typeSpecBuilder); 109 110 typeSpecBuilder.addMethod(closeMethod()); 111 112 typeSpecBuilder.addMethod(MethodSpec.methodBuilder("builder") 113 .addModifiers(Modifier.PUBLIC, STATIC) 114 .returns(interfaceClassName().nestedClass("Builder")) 115 .addStatement("return new DefaultBuilder()") 116 .build()); 117 118 typeSpecBuilder.addType(builder()); 119 typeSpecBuilder.addMethod(applyWaitersUserAgentMethod(poetExtensions, model)); 120 return typeSpecBuilder.build(); 121 } 122 closeMethod()123 private MethodSpec closeMethod() { 124 return MethodSpec.methodBuilder("close") 125 .addAnnotation(Override.class) 126 .addModifiers(PUBLIC) 127 .addStatement("managedResources.close()") 128 .build(); 129 } 130 clientClassName()131 protected abstract ClassName clientClassName(); 132 getWaiterResponseType(OperationModel opModel)133 protected abstract TypeName getWaiterResponseType(OperationModel opModel); 134 interfaceClassName()135 protected abstract ClassName interfaceClassName(); 136 additionalTypeSpecModification(TypeSpec.Builder type)137 protected void additionalTypeSpecModification(TypeSpec.Builder type) { 138 // no-op 139 } 140 additionalConstructorInitialization(MethodSpec.Builder method)141 protected void additionalConstructorInitialization(MethodSpec.Builder method) { 142 // no-op 143 } 144 additionalBuilderTypeSpecModification(TypeSpec.Builder builder)145 protected void additionalBuilderTypeSpecModification(TypeSpec.Builder builder) { 146 // no-op 147 } 148 additionalWaiterConfig()149 protected Optional<String> additionalWaiterConfig() { 150 return Optional.empty(); 151 } 152 constructor()153 private MethodSpec constructor() { 154 MethodSpec.Builder ctor = MethodSpec.constructorBuilder() 155 .addModifiers(PRIVATE) 156 .addParameter(className().nestedClass("DefaultBuilder"), "builder"); 157 ctor.addStatement("$T attributeMapBuilder = $T.builder()", ClassName.get(AttributeMap.class).nestedClass("Builder"), 158 AttributeMap.class); 159 ctor.beginControlFlow("if (builder.client == null)") 160 .addStatement("this.client = $T.builder().build()", clientClassName()) 161 .addStatement("attributeMapBuilder.put(CLIENT_ATTRIBUTE, this.client)") 162 .endControlFlow(); 163 ctor.beginControlFlow("else") 164 .addStatement("this.client = builder.client") 165 .endControlFlow(); 166 167 additionalConstructorInitialization(ctor); 168 169 ctor.addStatement("managedResources = attributeMapBuilder.build()"); 170 171 waiters.entrySet().stream() 172 .map(this::waiterFieldInitialization) 173 .forEach(ctor::addCode); 174 175 return ctor.build(); 176 } 177 waiterConfigInitializers()178 private List<MethodSpec> waiterConfigInitializers() { 179 List<MethodSpec> initializers = new ArrayList<>(); 180 waiters.forEach((k, v) -> initializers.add(waiterConfigInitializer(k, v))); 181 return initializers; 182 } 183 waiterConfigInitializer(String waiterKey, WaiterDefinition waiterDefinition)184 private MethodSpec waiterConfigInitializer(String waiterKey, WaiterDefinition waiterDefinition) { 185 ClassName overrideConfig = ClassName.get(WaiterOverrideConfiguration.class); 186 MethodSpec.Builder configMethod = 187 MethodSpec.methodBuilder(waiterFieldName(waiterKey) + "Config") 188 .addModifiers(PRIVATE, STATIC) 189 .addParameter(overrideConfig, "overrideConfig") 190 .returns(overrideConfig); 191 192 configMethod.addStatement("$T<$T> optionalOverrideConfig = Optional.ofNullable(overrideConfig)", 193 Optional.class, 194 WaiterOverrideConfiguration.class); 195 configMethod.addStatement("int maxAttempts = optionalOverrideConfig.flatMap(WaiterOverrideConfiguration::maxAttempts)" 196 + ".orElse($L)", 197 waiterDefinition.getMaxAttempts()); 198 configMethod.addStatement("$T backoffStrategy = optionalOverrideConfig." 199 + "flatMap(WaiterOverrideConfiguration::backoffStrategy).orElse($T.create($T.ofSeconds($L)))", 200 BackoffStrategy.class, 201 FixedDelayBackoffStrategy.class, 202 Duration.class, 203 waiterDefinition.getDelay()); 204 configMethod.addStatement("$T waitTimeout = optionalOverrideConfig.flatMap(WaiterOverrideConfiguration::waitTimeout)" 205 + ".orElse(null)", 206 Duration.class); 207 208 configMethod.addStatement("return WaiterOverrideConfiguration.builder().maxAttempts(maxAttempts).backoffStrategy" 209 + "(backoffStrategy).waitTimeout(waitTimeout).build()"); 210 return configMethod.build(); 211 } 212 waiterFieldInitialization(Map.Entry<String, WaiterDefinition> waiterDefinition)213 private CodeBlock waiterFieldInitialization(Map.Entry<String, WaiterDefinition> waiterDefinition) { 214 String waiterKey = waiterDefinition.getKey(); 215 WaiterDefinition waiter = waiterDefinition.getValue(); 216 OperationModel opModel = operationModel(waiter); 217 CodeBlock.Builder codeBlockBuilder = CodeBlock 218 .builder(); 219 220 String waiterFieldName = waiterFieldName(waiterKey); 221 codeBlockBuilder.add("this.$L = $T.builder($T.class)" 222 + ".acceptors($LAcceptors()).overrideConfiguration($LConfig(builder.overrideConfiguration))", 223 waiterFieldName, 224 waiterClassName, 225 ClassName.get(modelPackage, opModel.getReturnType().getReturnType()), 226 waiterFieldName, 227 waiterFieldName); 228 229 additionalWaiterConfig().ifPresent(codeBlockBuilder::add); 230 codeBlockBuilder.addStatement(".build()"); 231 return codeBlockBuilder.build(); 232 } 233 waitersFields()234 private List<FieldSpec> waitersFields() { 235 return waiters.entrySet().stream() 236 .map(this::waiterField) 237 .collect(Collectors.toList()); 238 } 239 waiterField(Map.Entry<String, WaiterDefinition> waiterDefinition)240 private FieldSpec waiterField(Map.Entry<String, WaiterDefinition> waiterDefinition) { 241 OperationModel opModel = operationModel(waiterDefinition.getValue()); 242 ClassName pojoResponse = ClassName.get(modelPackage, opModel.getReturnType().getReturnType()); 243 String fieldName = waiterFieldName(waiterDefinition.getKey()); 244 return FieldSpec.builder(ParameterizedTypeName.get(waiterClassName, 245 pojoResponse), fieldName) 246 .addModifiers(PRIVATE, FINAL) 247 .build(); 248 } 249 builder()250 private TypeSpec builder() { 251 TypeSpec.Builder builder = TypeSpec.classBuilder("DefaultBuilder") 252 .addModifiers(PUBLIC, STATIC, FINAL) 253 .addSuperinterface(interfaceClassName().nestedClass("Builder")) 254 .addField(clientClassName(), "client", PRIVATE) 255 .addField(ClassName.get(WaiterOverrideConfiguration.class), 256 "overrideConfiguration", PRIVATE); 257 258 additionalBuilderTypeSpecModification(builder); 259 builder.addMethods(builderMethods()); 260 builder.addMethod(MethodSpec.constructorBuilder() 261 .addModifiers(PRIVATE) 262 .build()); 263 return builder.build(); 264 } 265 builderMethods()266 private List<MethodSpec> builderMethods() { 267 List<MethodSpec> methods = new ArrayList<>(); 268 methods.add(MethodSpec.methodBuilder("overrideConfiguration") 269 .addModifiers(Modifier.PUBLIC) 270 .addAnnotation(Override.class) 271 .addParameter(ClassName.get(WaiterOverrideConfiguration.class), "overrideConfiguration") 272 .addStatement("this.overrideConfiguration = overrideConfiguration") 273 .addStatement("return this") 274 .returns(interfaceClassName().nestedClass("Builder")) 275 .build()); 276 methods.add(MethodSpec.methodBuilder("client") 277 .addModifiers(Modifier.PUBLIC) 278 .addAnnotation(Override.class) 279 .addParameter(clientClassName(), "client") 280 .addStatement("this.client = client") 281 .addStatement("return this") 282 .returns(interfaceClassName().nestedClass("Builder")) 283 .build()); 284 methods.add(MethodSpec.methodBuilder("build") 285 .addModifiers(Modifier.PUBLIC) 286 .returns(interfaceClassName()) 287 .addStatement("return new $T(this)", className()) 288 .build()); 289 return methods; 290 291 } 292 waiterOperations()293 private List<MethodSpec> waiterOperations() { 294 return waiters.entrySet() 295 .stream() 296 .flatMap(this::waiterOperations) 297 .sorted(Comparator.comparing(m -> m.name)) 298 .collect(Collectors.toList()); 299 } 300 waiterOperations(Map.Entry<String, WaiterDefinition> waiterDefinition)301 private Stream<MethodSpec> waiterOperations(Map.Entry<String, WaiterDefinition> waiterDefinition) { 302 List<MethodSpec> methods = new ArrayList<>(); 303 methods.add(waiterOperation(waiterDefinition)); 304 methods.add(waiterOperationWithOverrideConfig(waiterDefinition)); 305 return methods.stream(); 306 } 307 waiterOperationWithOverrideConfig(Map.Entry<String, WaiterDefinition> waiterDefinition)308 private MethodSpec waiterOperationWithOverrideConfig(Map.Entry<String, WaiterDefinition> waiterDefinition) { 309 String waiterMethodName = waiterDefinition.getKey(); 310 OperationModel opModel = operationModel(waiterDefinition.getValue()); 311 312 ClassName overrideConfig = ClassName.get(WaiterOverrideConfiguration.class); 313 ClassName requestType = ClassName.get(modelPackage, opModel.getInput().getVariableType()); 314 315 String waiterFieldName = waiterFieldName(waiterDefinition.getKey()); 316 MethodSpec.Builder builder = methodSignatureWithReturnType(waiterMethodName, opModel) 317 .addParameter(requestType, opModel.getInput().getVariableName()) 318 .addParameter(overrideConfig, "overrideConfig") 319 .addModifiers(PUBLIC) 320 .addAnnotation(Override.class) 321 .addStatement("return $L.$L(() -> client.$N(applyWaitersUserAgent($N)), $LConfig(overrideConfig))", 322 waiterFieldName, 323 waiterClassName.simpleName().equals("Waiter") ? "run" : "runAsync", 324 lowercaseFirstChar(waiterDefinition.getValue().getOperation()), 325 opModel.getInput().getVariableName(), 326 waiterFieldName); 327 328 return builder.build(); 329 } 330 waiterOperation(Map.Entry<String, WaiterDefinition> waiterDefinition)331 private MethodSpec waiterOperation(Map.Entry<String, WaiterDefinition> waiterDefinition) { 332 String waiterMethodName = waiterDefinition.getKey(); 333 OperationModel opModel = operationModel(waiterDefinition.getValue()); 334 335 ClassName requestType = ClassName.get(modelPackage, opModel.getInput().getVariableType()); 336 337 MethodSpec.Builder builder = methodSignatureWithReturnType(waiterMethodName, opModel) 338 .addParameter(requestType, opModel.getInput().getVariableName()) 339 .addModifiers(PUBLIC) 340 .addAnnotation(Override.class) 341 .addStatement("return $L.$L(() -> client.$N(applyWaitersUserAgent($N)))", 342 waiterFieldName(waiterMethodName), 343 waiterClassName.simpleName().equals("Waiter") ? "run" : "runAsync", 344 lowercaseFirstChar(waiterDefinition.getValue().getOperation()), 345 opModel.getInput().getVariableName()); 346 347 return builder.build(); 348 } 349 waiterAcceptorInitializers()350 private List<MethodSpec> waiterAcceptorInitializers() { 351 List<MethodSpec> initializers = new ArrayList<>(); 352 waiters.forEach((k, v) -> initializers.add(acceptorInitializer(k, v))); 353 return initializers; 354 } 355 acceptorInitializer(String waiterKey, WaiterDefinition waiterDefinition)356 private MethodSpec acceptorInitializer(String waiterKey, WaiterDefinition waiterDefinition) { 357 MethodSpec.Builder acceptorsMethod = 358 MethodSpec.methodBuilder(waiterFieldName(waiterKey) + "Acceptors") 359 .addModifiers(PRIVATE, STATIC) 360 .returns(waiterAcceptorTypeName(waiterDefinition)); 361 362 acceptorsMethod.addStatement("$T result = new $T<>()", waiterAcceptorTypeName(waiterDefinition), ArrayList.class); 363 364 for (Acceptor acceptor : waiterDefinition.getAcceptors()) { 365 acceptorsMethod.addCode("result.add(") 366 .addCode(acceptor(acceptor)) 367 .addCode(");"); 368 } 369 370 acceptorsMethod.addStatement("result.addAll($T.DEFAULT_ACCEPTORS)", waitersRuntimeClass()); 371 372 acceptorsMethod.addStatement("return result"); 373 374 return acceptorsMethod.build(); 375 } 376 waiterFieldName(String waiterKey)377 protected String waiterFieldName(String waiterKey) { 378 return lowercaseFirstChar(waiterKey) + "Waiter"; 379 } 380 operationModel(WaiterDefinition waiterDefinition)381 private OperationModel operationModel(WaiterDefinition waiterDefinition) { 382 return model.getOperation(waiterDefinition.getOperation()); 383 } 384 methodSignatureWithReturnType(String waiterMethodName, OperationModel opModel)385 private MethodSpec.Builder methodSignatureWithReturnType(String waiterMethodName, OperationModel opModel) { 386 return MethodSpec.methodBuilder(getWaiterMethodName(waiterMethodName)) 387 .returns(getWaiterResponseType(opModel)); 388 } 389 applyWaitersUserAgentMethod(PoetExtension poetExtensions, IntermediateModel model)390 static MethodSpec applyWaitersUserAgentMethod(PoetExtension poetExtensions, IntermediateModel model) { 391 392 TypeVariableName typeVariableName = 393 TypeVariableName.get("T", poetExtensions.getModelClass(model.getSdkRequestBaseClassName())); 394 395 ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName 396 .get(ClassName.get(Consumer.class), ClassName.get(AwsRequestOverrideConfiguration.Builder.class)); 397 398 CodeBlock codeBlock = CodeBlock.builder() 399 .addStatement("$T userAgentApplier = b -> b.addApiName($T.builder().version" 400 + "($S).name($S).build())", 401 parameterizedTypeName, ApiName.class, 402 WAITERS_USER_AGENT, 403 "hll") 404 .addStatement("$T overrideConfiguration =\n" 405 + " request.overrideConfiguration().map(c -> c.toBuilder()" 406 + ".applyMutation" 407 + "(userAgentApplier).build())\n" 408 + " .orElse((AwsRequestOverrideConfiguration.builder()" 409 + ".applyMutation" 410 + "(userAgentApplier).build()))", AwsRequestOverrideConfiguration.class) 411 .addStatement("return (T) request.toBuilder().overrideConfiguration" 412 + "(overrideConfiguration).build()") 413 .build(); 414 415 return MethodSpec.methodBuilder("applyWaitersUserAgent") 416 .addModifiers(Modifier.PRIVATE) 417 .addParameter(typeVariableName, "request") 418 .addTypeVariable(typeVariableName) 419 .addCode(codeBlock) 420 .returns(typeVariableName) 421 .build(); 422 } 423 getWaiterMethodName(String waiterMethodName)424 private String getWaiterMethodName(String waiterMethodName) { 425 return "waitUntil" + waiterMethodName; 426 } 427 waiterAcceptorTypeName(WaiterDefinition waiterDefinition)428 private TypeName waiterAcceptorTypeName(WaiterDefinition waiterDefinition) { 429 WildcardTypeName wildcardTypeName = WildcardTypeName.supertypeOf(fullyQualifiedResponseType(waiterDefinition)); 430 431 return ParameterizedTypeName.get(ClassName.get(List.class), 432 ParameterizedTypeName.get(ClassName.get(WaiterAcceptor.class), wildcardTypeName)); 433 } 434 fullyQualifiedResponseType(WaiterDefinition waiterDefinition)435 private TypeName fullyQualifiedResponseType(WaiterDefinition waiterDefinition) { 436 String modelPackage = model.getMetadata().getFullModelPackageName(); 437 String operationResponseType = model.getOperation(waiterDefinition.getOperation()).getReturnType().getReturnType(); 438 return ClassName.get(modelPackage, operationResponseType); 439 } 440 acceptor(Acceptor acceptor)441 private CodeBlock acceptor(Acceptor acceptor) { 442 CodeBlock.Builder result = CodeBlock.builder(); 443 444 switch (acceptor.getState()) { 445 case "success": 446 result.add("$T.success", WaiterAcceptor.class); 447 break; 448 case "failure": 449 result.add("$T.error", WaiterAcceptor.class); 450 break; 451 case "retry": 452 result.add("$T.retry", WaiterAcceptor.class); 453 break; 454 default: 455 throw new IllegalArgumentException("Unsupported acceptor state: " + acceptor.getState()); 456 } 457 458 switch (acceptor.getMatcher()) { 459 case "path": 460 result.add("OnResponseAcceptor("); 461 result.add(pathAcceptorBody(acceptor)); 462 result.add(")"); 463 break; 464 case "pathAll": 465 result.add("OnResponseAcceptor("); 466 result.add(pathAllAcceptorBody(acceptor)); 467 result.add(")"); 468 break; 469 case "pathAny": 470 result.add("OnResponseAcceptor("); 471 result.add(pathAnyAcceptorBody(acceptor)); 472 result.add(")"); 473 break; 474 case "status": 475 // Note: Ignores the result we've built so far because this uses a special acceptor implementation. 476 int expected = Integer.parseInt(acceptor.getExpected().asText()); 477 return CodeBlock.of("new $T($L, $T.$L)", waitersRuntimeClass().nestedClass("ResponseStatusAcceptor"), 478 expected, WaiterState.class, waiterState(acceptor)); 479 case "error": 480 result.add("OnExceptionAcceptor("); 481 result.add(errorAcceptorBody(acceptor)); 482 result.add(")"); 483 break; 484 default: 485 throw new IllegalArgumentException("Unsupported acceptor matcher: " + acceptor.getMatcher()); 486 } 487 488 return result.build(); 489 } 490 waiterState(Acceptor acceptor)491 private String waiterState(Acceptor acceptor) { 492 switch (acceptor.getState()) { 493 case "success": 494 return WaiterState.SUCCESS.name(); 495 case "failure": 496 return WaiterState.FAILURE.name(); 497 case "retry": 498 return WaiterState.RETRY.name(); 499 default: 500 throw new IllegalArgumentException("Unsupported acceptor state: " + acceptor.getState()); 501 } 502 } 503 pathAcceptorBody(Acceptor acceptor)504 private CodeBlock pathAcceptorBody(Acceptor acceptor) { 505 String expected = acceptor.getExpected().asText(); 506 String expectedType = acceptor.getExpected() instanceof JrsString ? "$S" : "$L"; 507 return CodeBlock.builder() 508 .add("response -> {") 509 .add("$1T input = new $1T(response);", waitersRuntimeClass().nestedClass("Value")) 510 .add("return $T.equals(", Objects.class) 511 .add(jmesPathAcceptorGenerator.interpret(acceptor.getArgument(), "input")) 512 .add(".value(), " + expectedType + ");", expected) 513 .add("}") 514 .build(); 515 } 516 pathAllAcceptorBody(Acceptor acceptor)517 private CodeBlock pathAllAcceptorBody(Acceptor acceptor) { 518 String expected = acceptor.getExpected().asText(); 519 String expectedType = acceptor.getExpected() instanceof JrsString ? "$S" : "$L"; 520 return CodeBlock.builder() 521 .add("response -> {") 522 .add("$1T input = new $1T(response);", waitersRuntimeClass().nestedClass("Value")) 523 .add("$T<$T> resultValues = ", List.class, Object.class) 524 .add(jmesPathAcceptorGenerator.interpret(acceptor.getArgument(), "input")) 525 .add(".values();") 526 .add("return !resultValues.isEmpty() && " 527 + "resultValues.stream().allMatch(v -> $T.equals(v, " + expectedType + "));", 528 Objects.class, expected) 529 .add("}") 530 .build(); 531 } 532 pathAnyAcceptorBody(Acceptor acceptor)533 private CodeBlock pathAnyAcceptorBody(Acceptor acceptor) { 534 String expected = acceptor.getExpected().asText(); 535 String expectedType = acceptor.getExpected() instanceof JrsString ? "$S" : "$L"; 536 return CodeBlock.builder() 537 .add("response -> {") 538 .add("$1T input = new $1T(response);", waitersRuntimeClass().nestedClass("Value")) 539 .add("$T<$T> resultValues = ", List.class, Object.class) 540 .add(jmesPathAcceptorGenerator.interpret(acceptor.getArgument(), "input")) 541 .add(".values();") 542 .add("return !resultValues.isEmpty() && " 543 + "resultValues.stream().anyMatch(v -> $T.equals(v, " + expectedType + "));", 544 Objects.class, expected) 545 .add("}") 546 .build(); 547 } 548 errorAcceptorBody(Acceptor acceptor)549 private CodeBlock errorAcceptorBody(Acceptor acceptor) { 550 String expected = acceptor.getExpected().asText(); 551 String expectedType = acceptor.getExpected() instanceof JrsString ? "$S" : "$L"; 552 return CodeBlock.of("error -> $T.equals(errorCode(error), " + expectedType + ")", Objects.class, expected); 553 } 554 staticErrorCodeMethod()555 private MethodSpec staticErrorCodeMethod() { 556 return MethodSpec.methodBuilder("errorCode") 557 .addModifiers(PRIVATE, STATIC) 558 .returns(String.class) 559 .addParameter(Throwable.class, "error") 560 .addCode("if (error instanceof $T) {", AwsServiceException.class) 561 .addCode("return (($T) error).awsErrorDetails().errorCode();", AwsServiceException.class) 562 .addCode("}") 563 .addCode("return null;") 564 .build(); 565 } 566 waitersRuntimeClass()567 private ClassName waitersRuntimeClass() { 568 return ClassName.get(model.getMetadata().getFullWaitersInternalPackageName(), 569 WaitersRuntimeGeneratorTask.RUNTIME_CLASS_NAME); 570 } 571 } 572