|
26 | 26 | import com.squareup.javapoet.WildcardTypeName;
|
27 | 27 | import java.util.List;
|
28 | 28 | import java.util.Optional;
|
| 29 | +import java.util.concurrent.CompletableFuture; |
29 | 30 | import java.util.stream.Collectors;
|
30 | 31 | import javax.lang.model.element.Modifier;
|
31 | 32 | import software.amazon.awssdk.awscore.eventstream.EventStreamAsyncResponseTransformer;
|
|
42 | 43 | import software.amazon.awssdk.codegen.poet.PoetExtensions;
|
43 | 44 | import software.amazon.awssdk.codegen.poet.eventstream.EventStreamUtils;
|
44 | 45 | import software.amazon.awssdk.core.SdkResponse;
|
45 |
| -import software.amazon.awssdk.core.async.AsyncResponseTransformer; |
46 | 46 | import software.amazon.awssdk.core.client.handler.ClientExecutionParams;
|
47 | 47 | import software.amazon.awssdk.core.http.HttpResponseHandler;
|
48 | 48 | import software.amazon.awssdk.core.internal.protocol.json.VoidJsonUnmarshaller;
|
@@ -213,38 +213,105 @@ public CodeBlock asyncExecutionHandler(OperationModel opModel) {
|
213 | 213 | : "";
|
214 | 214 | CodeBlock.Builder builder = CodeBlock.builder();
|
215 | 215 | if (opModel.hasEventStreamOutput()) {
|
216 |
| - builder.add("$T<$T, $T> asyncResponseTransformer = new $T<>(\n" |
217 |
| - + "asyncResponseHandler, responseHandler, eventResponseHandler, errorResponseHandler, serviceName());\n", |
218 |
| - ClassName.get(AsyncResponseTransformer.class), |
219 |
| - ClassName.get(SdkResponse.class), |
220 |
| - ClassName.get(Void.class), |
221 |
| - ClassName.get(EventStreamAsyncResponseTransformer.class)); |
| 216 | + ClassName eventStreamBaseClass = EventStreamUtils.create(poetExtensions, opModel).eventStreamBaseClass(); |
| 217 | + ParameterizedTypeName transformerType = ParameterizedTypeName.get( |
| 218 | + ClassName.get(EventStreamAsyncResponseTransformer.class), pojoResponseType, eventStreamBaseClass); |
| 219 | + builder.addStatement("$1T<$2T> future = new $1T<>()", |
| 220 | + ClassName.get(CompletableFuture.class), |
| 221 | + ClassName.get(Void.class)); |
| 222 | + builder.add("$T asyncResponseTransformer = $T.<$T, $T>builder()\n" + |
| 223 | + " .eventStreamResponseHandler(asyncResponseHandler)\n" |
| 224 | + + " .eventResponseHandler(eventResponseHandler)\n" |
| 225 | + + " .initialResponseHandler(responseHandler)\n" |
| 226 | + + " .exceptionResponseHandler(errorResponseHandler)\n" |
| 227 | + + " .future(future)\n" |
| 228 | + + " .executor(executor)\n" |
| 229 | + + " .serviceName(serviceName())\n" |
| 230 | + + " .build();", |
| 231 | + transformerType, |
| 232 | + ClassName.get(EventStreamAsyncResponseTransformer.class), |
| 233 | + pojoResponseType, |
| 234 | + eventStreamBaseClass); |
222 | 235 | }
|
223 | 236 | boolean isStreaming = opModel.hasStreamingOutput() || opModel.hasEventStreamOutput();
|
224 | 237 | String protocolFactory = opModel.hasEventStreamOutput() ? "jsonProtocolFactory" : "protocolFactory";
|
225 | 238 | String customerResponseHandler = opModel.hasEventStreamOutput() ? "asyncResponseHandler" : "asyncResponseTransformer";
|
226 |
| - return builder.add("\n\nreturn clientHandler.execute(new $T<$T, $T>()\n" + |
227 |
| - ".withMarshaller(new $T($L))\n" + |
228 |
| - ".withResponseHandler($L)\n" + |
229 |
| - ".withErrorResponseHandler(errorResponseHandler)\n" + |
230 |
| - asyncRequestBody + |
231 |
| - ".withInput($L)$L)$L;", |
232 |
| - ClientExecutionParams.class, |
233 |
| - requestType, |
234 |
| - opModel.hasEventStreamOutput() ? SdkResponse.class : pojoResponseType, |
235 |
| - marshaller, |
236 |
| - protocolFactory, |
237 |
| - opModel.hasEventStreamOutput() ? "voidResponseHandler" : "responseHandler", |
238 |
| - opModel.getInput().getVariableName(), |
239 |
| - isStreaming ? ", asyncResponseTransformer" : "", |
240 |
| - // If it's a streaming operation we also need to notify the handler on exception. |
241 |
| - isStreaming ? String.format(".whenComplete((r, e) -> {%n" |
242 |
| - + " if (e != null) {%n" |
243 |
| - + " %s.exceptionOccurred(e);%n" |
244 |
| - + " }%n" |
245 |
| - + "})", customerResponseHandler) |
246 |
| - : "") |
247 |
| - .build(); |
| 239 | + builder.add("\n\n$L clientHandler.execute(new $T<$T, $T>()\n" + |
| 240 | + ".withMarshaller(new $T($L))\n" + |
| 241 | + ".withResponseHandler($L)\n" + |
| 242 | + ".withErrorResponseHandler(errorResponseHandler)\n" + |
| 243 | + asyncRequestBody + |
| 244 | + ".withInput($L)$L)$L;", |
| 245 | + // If the operation has an event stream output we use a different future so we don't return the one |
| 246 | + // from the client. |
| 247 | + opModel.hasEventStreamOutput() ? "" : "return", |
| 248 | + ClientExecutionParams.class, |
| 249 | + requestType, |
| 250 | + opModel.hasEventStreamOutput() ? SdkResponse.class : pojoResponseType, |
| 251 | + marshaller, |
| 252 | + protocolFactory, |
| 253 | + opModel.hasEventStreamOutput() ? "voidResponseHandler" : "responseHandler", |
| 254 | + opModel.getInput().getVariableName(), |
| 255 | + isStreaming ? ", asyncResponseTransformer" : "", |
| 256 | + whenCompleteBody(opModel, customerResponseHandler)); |
| 257 | + if (opModel.hasEventStreamOutput()) { |
| 258 | + builder.addStatement("return future"); |
| 259 | + } |
| 260 | + return builder.build(); |
| 261 | + } |
| 262 | + |
| 263 | + /** |
| 264 | + * For streaming operations we need to notify the response handler or response transformer on exception so |
| 265 | + * we add a .whenComplete to the future. |
| 266 | + * |
| 267 | + * @param operationModel Op model. |
| 268 | + * @param responseHandlerName Variable name of response handler customer passed in. |
| 269 | + * @return whenComplete to append to future. |
| 270 | + */ |
| 271 | + private String whenCompleteBody(OperationModel operationModel, String responseHandlerName) { |
| 272 | + if (operationModel.hasEventStreamOutput()) { |
| 273 | + return eventStreamOutputWhenComplete(responseHandlerName); |
| 274 | + } else if (operationModel.hasStreamingOutput()) { |
| 275 | + return streamingOutputWhenComplete(responseHandlerName); |
| 276 | + } else { |
| 277 | + // Non streaming can just return the future as is |
| 278 | + return ""; |
| 279 | + } |
| 280 | + } |
| 281 | + |
| 282 | + /** |
| 283 | + * Need to notify the response handler/response transformer if the future is completed exceptionally. |
| 284 | + * |
| 285 | + * @param responseHandlerName Variable name of response handler customer passed in. |
| 286 | + * @return whenComplete to append to future. |
| 287 | + */ |
| 288 | + private String streamingOutputWhenComplete(String responseHandlerName) { |
| 289 | + return String.format(".whenComplete((r, e) -> {%n" |
| 290 | + + " if (e != null) {%n" |
| 291 | + + " %s.exceptionOccurred(e);%n" |
| 292 | + + " }%n" |
| 293 | + + "})", responseHandlerName); |
| 294 | + } |
| 295 | + |
| 296 | + /** |
| 297 | + * For event streaming our future notification is a bit complicated. We create a different future that is not tied |
| 298 | + * to the lifecycle of the wire request. Successful completion of the future is signalled in |
| 299 | + * {@link EventStreamAsyncResponseTransformer}. Failure is notified via the normal future (the one returned by the client |
| 300 | + * handler). |
| 301 | + * |
| 302 | + * @param responseHandlerName Variable name of response handler customer passed in. |
| 303 | + * @return whenComplete to append to future. |
| 304 | + */ |
| 305 | + private String eventStreamOutputWhenComplete(String responseHandlerName) { |
| 306 | + return String.format(".whenComplete((r, e) -> {%n" |
| 307 | + + " if (e != null) {%n" |
| 308 | + + " try {" |
| 309 | + + " %s.exceptionOccurred(e);%n" |
| 310 | + + " } finally {" |
| 311 | + + " future.completeExceptionally(e);" |
| 312 | + + " }" |
| 313 | + + " }%n" |
| 314 | + + "})", responseHandlerName); |
248 | 315 | }
|
249 | 316 |
|
250 | 317 | private ClassName getUnmarshallerType(OperationModel opModel) {
|
|
0 commit comments