From db2a251179449707edca6dcc4071c4af40449b78 Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Sun, 28 Jun 2020 23:49:04 -0700 Subject: [PATCH] Mutiple metrics fixes - Allow lists of publishers to be set. Allow setting of metric pubishers to be set on the client of request override rather than a single publisher, making using multiple MetricPublishers per client or request easier. - Remove any non-core usage of MetricUtils. - Fix metrics test in TranscribeStreamIntegrationTest. - Fix Async API call metric stage to add the duration in a separate CompletionStage. --- .../codegen/poet/client/AsyncClientClass.java | 52 +++++- .../codegen/poet/client/SyncClientClass.java | 55 +++++- .../poet/client/specs/ProtocolSpec.java | 4 +- .../poet/client/test-async-client-class.java | 153 +++++++-------- .../client/test-endpoint-discovery-async.java | 63 ++++--- .../client/test-endpoint-discovery-sync.java | 44 +++-- .../poet/client/test-json-client-class.java | 68 ++++--- .../client/test-query-async-client-class.java | 64 ++++--- .../poet/client/test-query-client-class.java | 45 +++-- .../client/handler/AwsClientHandlerUtils.java | 10 +- .../core/RequestOverrideConfiguration.java | 48 +++-- .../builder/SdkDefaultClientBuilder.java | 8 +- .../config/ClientOverrideConfiguration.java | 45 +++-- .../core/client/config/SdkClientOption.java | 3 +- .../AsyncApiCallMetricCollectionStage.java | 15 +- .../core/internal/util/MetricUtils.java | 41 ---- .../RequestOverrideConfigurationTest.java | 72 +++++++ .../ClientOverrideConfigurationTest.java | 72 +++++++ .../AsyncHttpClientApiCallTimeoutTests.java | 1 + .../core/internal/util/MetricUtilsTest.java | 28 --- .../TranscribeStreamingIntegrationTest.java | 3 +- .../services/metrics/CoreMetricsTest.java | 4 +- ...ncClientMetricPublisherResolutionTest.java | 176 ++++++++++++++++++ ...ncClientMetricPublisherResolutionTest.java | 160 ++++++++++++++++ .../metrics/async/AsyncCoreMetricsTest.java | 4 +- .../AsyncEventStreamingCoreMetricsTest.java | 10 +- .../async/AsyncStreamingCoreMetricsTest.java | 2 +- .../async/BaseAsyncCoreMetricsTest.java | 7 +- 28 files changed, 925 insertions(+), 332 deletions(-) create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/SyncClientMetricPublisherResolutionTest.java create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncClientMetricPublisherResolutionTest.java diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java index 084c9415ed8d..1bfd0f2b9290 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java @@ -18,6 +18,7 @@ import static com.squareup.javapoet.TypeSpec.Builder; import static java.util.Collections.singletonList; import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.STATIC; import static software.amazon.awssdk.codegen.poet.client.ClientClassUtils.applyPaginatorUserAgentMethod; import static software.amazon.awssdk.codegen.poet.client.ClientClassUtils.applySignerOverrideMethod; import static software.amazon.awssdk.codegen.poet.client.SyncClientClass.getProtocolSpecs; @@ -31,8 +32,8 @@ import com.squareup.javapoet.TypeSpec; import java.net.URI; import java.nio.ByteBuffer; +import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.concurrent.Executor; import java.util.stream.Collectors; import javax.lang.model.element.Modifier; @@ -55,6 +56,7 @@ import software.amazon.awssdk.codegen.poet.StaticImport; import software.amazon.awssdk.codegen.poet.client.specs.ProtocolSpec; import software.amazon.awssdk.codegen.poet.eventstream.EventStreamUtils; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.async.SdkPublisher; import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; @@ -62,7 +64,6 @@ import software.amazon.awssdk.core.client.handler.AsyncClientHandler; import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache; import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.metrics.MetricCollector; import software.amazon.awssdk.metrics.MetricPublisher; @@ -71,7 +72,7 @@ import software.amazon.awssdk.utils.FunctionalUtils; public final class AsyncClientClass extends AsyncClientInterface { - private static final String PUBLISHER_NAME = "metricPublisher"; + private static final String PUBLISHER_NAME = "metricPublishers"; private static final String METRIC_COLLECTOR_NAME = "apiCallMetricCollector"; private final IntermediateModel model; private final PoetExtensions poetExtensions; @@ -108,7 +109,8 @@ public TypeSpec poetSpec() { .addMethods(operations()) .addMethod(closeMethod()) .addMethods(protocolSpec.additionalMethods()) - .addMethod(protocolSpec.initProtocolFactory(model)); + .addMethod(protocolSpec.initProtocolFactory(model)) + .addMethod(resolveMetricPublishersMethod()); // Kinesis doesn't support CBOR for STS yet so need another protocol factory for JSON if (model.getMetadata().isCborProtocol()) { @@ -240,13 +242,12 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation "() -> $N.exceptionOccurred(t))", paramName); } - builder.addStatement("$T<$T> $N = $T.resolvePublisher(clientConfiguration, $N.overrideConfiguration().orElse(null))", - Optional.class, + builder.addStatement("$T<$T> $N = resolveMetricPublishers(clientConfiguration, $N.overrideConfiguration().orElse(null))", + List.class, MetricPublisher.class, PUBLISHER_NAME, - MetricUtils.class, opModel.getInput().getVariableName()) - .addStatement("$N.ifPresent(p -> p.publish($N.collect()))", PUBLISHER_NAME, "apiCallMetricCollector") + .addStatement("$N.forEach(p -> p.publish($N.collect()))", PUBLISHER_NAME, "apiCallMetricCollector") .addStatement("return $T.failedFuture(t)", CompletableFutureUtils.class) .endControlFlow(); @@ -326,4 +327,39 @@ private MethodSpec utilitiesMethod() { String.join(",", config.getCreateMethodParams())) .build(); } + + private MethodSpec resolveMetricPublishersMethod() { + String clientConfigName = "clientConfiguration"; + String requestOverrideConfigName = "requestOverrideConfiguration"; + + MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("resolveMetricPublishers") + .addModifiers(PRIVATE, STATIC) + .returns(ParameterizedTypeName.get(List.class, MetricPublisher.class)) + .addParameter(SdkClientConfiguration.class, clientConfigName) + .addParameter(RequestOverrideConfiguration.class, requestOverrideConfigName); + + String publishersName = "publishers"; + + methodBuilder.addStatement("$T $N = null", ParameterizedTypeName.get(List.class, MetricPublisher.class), publishersName); + + methodBuilder.beginControlFlow("if ($N != null)", requestOverrideConfigName) + .addStatement("$N = $N.metricPublishers()", publishersName, requestOverrideConfigName) + .endControlFlow(); + + methodBuilder.beginControlFlow("if ($1N == null || $1N.isEmpty())", publishersName) + .addStatement("$N = $N.option($T.$N)", + publishersName, + clientConfigName, + SdkClientOption.class, + "METRIC_PUBLISHERS") + .endControlFlow(); + + methodBuilder.beginControlFlow("if ($1N == null)", publishersName) + .addStatement("$N = $T.emptyList()", publishersName, Collections.class) + .endControlFlow(); + + methodBuilder.addStatement("return $N", publishersName); + + return methodBuilder.build(); + } } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java index 3162d7b50d57..d330e2959eae 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java @@ -17,18 +17,20 @@ import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.STATIC; import static software.amazon.awssdk.codegen.poet.client.ClientClassUtils.applyPaginatorUserAgentMethod; import static software.amazon.awssdk.codegen.poet.client.ClientClassUtils.applySignerOverrideMethod; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeSpec.Builder; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; import javax.lang.model.element.Modifier; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -48,12 +50,12 @@ import software.amazon.awssdk.codegen.poet.client.specs.QueryProtocolSpec; import software.amazon.awssdk.codegen.poet.client.specs.XmlProtocolSpec; import software.amazon.awssdk.codegen.utils.PaginatorUtils; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.SyncClientHandler; import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache; import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.metrics.MetricCollector; import software.amazon.awssdk.metrics.MetricPublisher; @@ -89,7 +91,8 @@ public TypeSpec poetSpec() { .addMethod(constructor()) .addMethod(nameMethod()) .addMethods(protocolSpec.additionalMethods()) - .addMethods(operations()); + .addMethods(operations()) + .addMethod(resolveMetricPublishersMethod()); protocolSpec.createErrorResponseHandler().ifPresent(classBuilder::addMethod); @@ -196,7 +199,7 @@ private List operationMethodSpecs(OperationModel opModel) { method.addStatement("$1T $2N = $1T.create($3S)", MetricCollector.class, metricCollectorName, "ApiCall"); - String publisherName = "metricPublisher"; + String publishersName = "metricPublishers"; method.beginControlFlow("try") .addStatement("$N.reportMetric($T.$L, $S)", metricCollectorName, CoreMetric.class, "SERVICE_ID", @@ -206,13 +209,12 @@ private List operationMethodSpecs(OperationModel opModel) { .addCode(protocolSpec.executionHandler(opModel)) .endControlFlow() .beginControlFlow("finally") - .addStatement("$T<$T> $N = $T.resolvePublisher(clientConfiguration, $N)", - Optional.class, + .addStatement("$T<$T> $N = resolveMetricPublishers(clientConfiguration, $N.overrideConfiguration().orElse(null))", + List.class, MetricPublisher.class, - publisherName, - MetricUtils.class, + publishersName, opModel.getInput().getVariableName()) - .addStatement("$N.ifPresent(p -> p.publish($N.collect()))", publisherName, metricCollectorName) + .addStatement("$N.forEach(p -> p.publish($N.collect()))", publishersName, metricCollectorName) .endControlFlow(); methods.add(method.build()); @@ -286,4 +288,39 @@ static ProtocolSpec getProtocolSpecs(PoetExtensions poetExtensions, Intermediate throw new RuntimeException("Unknown protocol: " + protocol.name()); } } + + private MethodSpec resolveMetricPublishersMethod() { + String clientConfigName = "clientConfiguration"; + String requestOverrideConfigName = "requestOverrideConfiguration"; + + MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("resolveMetricPublishers") + .addModifiers(PRIVATE, STATIC) + .returns(ParameterizedTypeName.get(List.class, MetricPublisher.class)) + .addParameter(SdkClientConfiguration.class, clientConfigName) + .addParameter(RequestOverrideConfiguration.class, requestOverrideConfigName); + + String publishersName = "publishers"; + + methodBuilder.addStatement("$T $N = null", ParameterizedTypeName.get(List.class, MetricPublisher.class), publishersName); + + methodBuilder.beginControlFlow("if ($N != null)", requestOverrideConfigName) + .addStatement("$N = $N.metricPublishers()", publishersName, requestOverrideConfigName) + .endControlFlow(); + + methodBuilder.beginControlFlow("if ($1N == null || $1N.isEmpty())", publishersName) + .addStatement("$N = $N.option($T.$N)", + publishersName, + clientConfigName, + SdkClientOption.class, + "METRIC_PUBLISHERS") + .endControlFlow(); + + methodBuilder.beginControlFlow("if ($1N == null)", publishersName) + .addStatement("$N = $T.emptyList()", publishersName, Collections.class) + .endControlFlow(); + + methodBuilder.addStatement("return $N", publishersName); + + return methodBuilder.build(); + } } diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/ProtocolSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/ProtocolSpec.java index 3cd7191e455e..f374500e8997 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/ProtocolSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/ProtocolSpec.java @@ -186,8 +186,8 @@ default String publishMetricsWhenComplete() { } default String publishMetrics() { - return "Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, " + return "List metricPublishers = resolveMetricPublishers(clientConfiguration, " + "requestOverrideConfig);\n" - + "metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect()));"; + + "metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect()));"; } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java index c97b268bece4..908c6de7b7f3 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-async-client-class.java @@ -3,7 +3,8 @@ import static software.amazon.awssdk.utils.FunctionalUtils.runAndLogError; import java.nio.ByteBuffer; -import java.util.Optional; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -23,6 +24,7 @@ import software.amazon.awssdk.awscore.eventstream.RestEventStreamAsyncResponseTransformer; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.core.ApiName; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.SdkPojoBuilder; import software.amazon.awssdk.core.SdkResponse; import software.amazon.awssdk.core.async.AsyncRequestBody; @@ -30,11 +32,11 @@ import software.amazon.awssdk.core.async.SdkPublisher; import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.AsyncClientHandler; import software.amazon.awssdk.core.client.handler.AttachHttpMetadataResponseHandler; import software.amazon.awssdk.core.client.handler.ClientExecutionParams; import software.amazon.awssdk.core.http.HttpResponseHandler; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.protocol.VoidSdkResponse; import software.amazon.awssdk.core.runtime.transform.AsyncStreamingRequestMarshaller; @@ -181,15 +183,14 @@ public CompletableFuture aPostOperation(APostOperationRe .withInput(aPostOperationRequest)); AwsRequestOverrideConfiguration requestOverrideConfig = aPostOperationRequest.overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, aPostOperationRequest + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationRequest .overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -242,15 +243,14 @@ public CompletableFuture aPostOperationWithOut AwsRequestOverrideConfiguration requestOverrideConfig = aPostOperationWithOutputRequest.overrideConfiguration() .orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - aPostOperationWithOutputRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationWithOutputRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -333,17 +333,16 @@ public CompletableFuture eventStreamOperation(EventStreamOperationRequest future.completeExceptionally(e); } } - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return CompletableFutureUtils.forwardExceptionTo(future, executeFuture); } catch (Throwable t) { runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseHandler.exceptionOccurred(t)); - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - eventStreamOperationRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, eventStreamOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -403,15 +402,14 @@ public CompletableFuture eventStreamO AwsRequestOverrideConfiguration requestOverrideConfig = eventStreamOperationWithOnlyInputRequest .overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, eventStreamOperationWithOnlyInputRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -490,17 +488,16 @@ public CompletableFuture eventStreamOperationWithOnlyOutput( future.completeExceptionally(e); } } - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return CompletableFutureUtils.forwardExceptionTo(future, executeFuture); } catch (Throwable t) { runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseHandler.exceptionOccurred(t)); - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, eventStreamOperationWithOnlyOutputRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -553,15 +550,14 @@ public CompletableFuture getWithoutRequiredMe AwsRequestOverrideConfiguration requestOverrideConfig = getWithoutRequiredMembersRequest.overrideConfiguration() .orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, getWithoutRequiredMembersRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -611,15 +607,14 @@ public CompletableFuture paginatedOpera AwsRequestOverrideConfiguration requestOverrideConfig = paginatedOperationWithResultKeyRequest .overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, paginatedOperationWithResultKeyRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -646,7 +641,7 @@ public CompletableFuture paginatedOpera * The following are few ways to use the response class: *

* 1) Using the subscribe helper method - * + * *
      * {@code
      * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
@@ -656,19 +651,19 @@ public CompletableFuture paginatedOpera
      * 
* * 2) Using a custom subscriber - * + * *
      * {@code
      * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithResultKeyPublisher publisher = client.paginatedOperationWithResultKeyPaginator(request);
      * publisher.subscribe(new Subscriber() {
-     *
+     * 
      * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
-     *
-     *
+     * 
+     * 
      * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithResultKeyResponse response) { //... };
      * });}
      * 
- * + * * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2. *

* Please notice that the configuration of MaxResults won't limit the number of results you get with the @@ -746,15 +741,14 @@ public CompletableFuture paginatedOp AwsRequestOverrideConfiguration requestOverrideConfig = paginatedOperationWithoutResultKeyRequest .overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, paginatedOperationWithoutResultKeyRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -781,7 +775,7 @@ public CompletableFuture paginatedOp * The following are few ways to use the response class: *

* 1) Using the subscribe helper method - * + * *
      * {@code
      * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
@@ -791,19 +785,19 @@ public CompletableFuture paginatedOp
      * 
* * 2) Using a custom subscriber - * + * *
      * {@code
      * software.amazon.awssdk.services.json.paginators.PaginatedOperationWithoutResultKeyPublisher publisher = client.paginatedOperationWithoutResultKeyPaginator(request);
      * publisher.subscribe(new Subscriber() {
-     *
+     * 
      * public void onSubscribe(org.reactivestreams.Subscriber subscription) { //... };
-     *
-     *
+     * 
+     * 
      * public void onNext(software.amazon.awssdk.services.json.model.PaginatedOperationWithoutResultKeyResponse response) { //... };
      * });}
      * 
- * + * * As the response is a publisher, it can work well with third party reactive streams implementations like RxJava2. *

* Please notice that the configuration of MaxResults won't limit the number of results you get with the @@ -889,15 +883,14 @@ public CompletableFuture streamingInputOperatio AwsRequestOverrideConfiguration requestOverrideConfig = streamingInputOperationRequest.overrideConfiguration() .orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingInputOperationRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingInputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -969,17 +962,16 @@ public CompletableFuture streamingInputOutputOperation( runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseTransformer.exceptionOccurred(e)); } - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseTransformer.exceptionOccurred(t)); - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingInputOutputOperationRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -1039,17 +1031,16 @@ public CompletableFuture streamingOutputOperation( runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseTransformer.exceptionOccurred(e)); } - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseTransformer.exceptionOccurred(t)); - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingOutputOperationRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingOutputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -1070,6 +1061,21 @@ private > T init(T builder) { .exceptionBuilderSupplier(InvalidInputException::builder).httpStatusCode(400).build()); } + private static List resolveMetricPublishers(SdkClientConfiguration clientConfiguration, + RequestOverrideConfiguration requestOverrideConfiguration) { + List publishers = null; + if (requestOverrideConfiguration != null) { + publishers = requestOverrideConfiguration.metricPublishers(); + } + if (publishers == null || publishers.isEmpty()) { + publishers = clientConfiguration.option(SdkClientOption.METRIC_PUBLISHERS); + } + if (publishers == null) { + publishers = Collections.emptyList(); + } + return publishers; + } + private T applyPaginatorUserAgent(T request) { Consumer userAgentApplier = b -> b.addApiName(ApiName.builder() .version(VersionInfo.SDK_VERSION).name("PAGINATED").build()); @@ -1100,3 +1106,4 @@ private HttpResponseHandler createErrorResponseHandler(Base return protocolFactory.createErrorResponseHandler(operationMetadata); } } + diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-async.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-async.java index 6a19b2018eea..6fafd5e9715e 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-async.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-async.java @@ -3,7 +3,8 @@ import static software.amazon.awssdk.utils.FunctionalUtils.runAndLogError; import java.net.URI; -import java.util.Optional; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +14,7 @@ import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.client.handler.AwsAsyncClientHandler; import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.AsyncClientHandler; @@ -20,7 +22,6 @@ import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache; import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest; import software.amazon.awssdk.core.http.HttpResponseHandler; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.metrics.MetricCollector; import software.amazon.awssdk.metrics.MetricPublisher; @@ -116,15 +117,14 @@ public CompletableFuture describeEndpoints(DescribeEn .withMetricCollector(apiCallMetricCollector).withInput(describeEndpointsRequest)); AwsRequestOverrideConfiguration requestOverrideConfig = describeEndpointsRequest.overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - describeEndpointsRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, describeEndpointsRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -181,15 +181,14 @@ public CompletableFuture testDiscovery AwsRequestOverrideConfiguration requestOverrideConfig = testDiscoveryIdentifiersRequiredRequest .overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, + List metricPublishers = resolveMetricPublishers(clientConfiguration, testDiscoveryIdentifiersRequiredRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -245,15 +244,14 @@ public CompletableFuture testDiscoveryOptional( AwsRequestOverrideConfiguration requestOverrideConfig = testDiscoveryOptionalRequest.overrideConfiguration().orElse( null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - testDiscoveryOptionalRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, testDiscoveryOptionalRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -309,15 +307,14 @@ public CompletableFuture testDiscoveryRequired( AwsRequestOverrideConfiguration requestOverrideConfig = testDiscoveryRequiredRequest.overrideConfiguration().orElse( null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - testDiscoveryRequiredRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, testDiscoveryRequiredRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -333,8 +330,24 @@ private > T init(T builder) { .protocolVersion("1.1"); } + private static List resolveMetricPublishers(SdkClientConfiguration clientConfiguration, + RequestOverrideConfiguration requestOverrideConfiguration) { + List publishers = null; + if (requestOverrideConfiguration != null) { + publishers = requestOverrideConfiguration.metricPublishers(); + } + if (publishers == null || publishers.isEmpty()) { + publishers = clientConfiguration.option(SdkClientOption.METRIC_PUBLISHERS); + } + if (publishers == null) { + publishers = Collections.emptyList(); + } + return publishers; + } + private HttpResponseHandler createErrorResponseHandler(BaseAwsJsonProtocolFactory protocolFactory, JsonOperationMetadata operationMetadata) { return protocolFactory.createErrorResponseHandler(operationMetadata); } } + diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-sync.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-sync.java index 42d335b9cb58..1f3fe65691ca 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-sync.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-sync.java @@ -1,12 +1,14 @@ package software.amazon.awssdk.services.endpointdiscoverytest; import java.net.URI; -import java.util.Optional; +import java.util.Collections; +import java.util.List; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler; import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.ClientExecutionParams; @@ -15,7 +17,6 @@ import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.http.HttpResponseHandler; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.metrics.MetricCollector; import software.amazon.awssdk.metrics.MetricPublisher; @@ -104,9 +105,9 @@ public DescribeEndpointsResponse describeEndpoints(DescribeEndpointsRequest desc .withMetricCollector(apiCallMetricCollector) .withMarshaller(new DescribeEndpointsRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - describeEndpointsRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, describeEndpointsRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -156,9 +157,9 @@ public TestDiscoveryIdentifiersRequiredResponse testDiscoveryIdentifiersRequired .withInput(testDiscoveryIdentifiersRequiredRequest).withMetricCollector(apiCallMetricCollector) .withMarshaller(new TestDiscoveryIdentifiersRequiredRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - testDiscoveryIdentifiersRequiredRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, + testDiscoveryIdentifiersRequiredRequest.overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -206,9 +207,9 @@ public TestDiscoveryOptionalResponse testDiscoveryOptional(TestDiscoveryOptional .withInput(testDiscoveryOptionalRequest).withMetricCollector(apiCallMetricCollector) .withMarshaller(new TestDiscoveryOptionalRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - testDiscoveryOptionalRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, testDiscoveryOptionalRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -256,12 +257,27 @@ public TestDiscoveryRequiredResponse testDiscoveryRequired(TestDiscoveryRequired .withInput(testDiscoveryRequiredRequest).withMetricCollector(apiCallMetricCollector) .withMarshaller(new TestDiscoveryRequiredRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - testDiscoveryRequiredRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, testDiscoveryRequiredRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } + private static List resolveMetricPublishers(SdkClientConfiguration clientConfiguration, + RequestOverrideConfiguration requestOverrideConfiguration) { + List publishers = null; + if (requestOverrideConfiguration != null) { + publishers = requestOverrideConfiguration.metricPublishers(); + } + if (publishers == null || publishers.isEmpty()) { + publishers = clientConfiguration.option(SdkClientOption.METRIC_PUBLISHERS); + } + if (publishers == null) { + publishers = Collections.emptyList(); + } + return publishers; + } + private HttpResponseHandler createErrorResponseHandler(BaseAwsJsonProtocolFactory protocolFactory, JsonOperationMetadata operationMetadata) { return protocolFactory.createErrorResponseHandler(operationMetadata); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java index e3c6d45ba48e..d5dd67826475 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-json-client-class.java @@ -1,6 +1,7 @@ package software.amazon.awssdk.services.json; -import java.util.Optional; +import java.util.Collections; +import java.util.List; import java.util.function.Consumer; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -9,12 +10,13 @@ import software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.core.ApiName; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.ClientExecutionParams; import software.amazon.awssdk.core.client.handler.SyncClientHandler; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.http.HttpResponseHandler; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.runtime.transform.StreamingRequestMarshaller; import software.amazon.awssdk.core.signer.Signer; @@ -129,8 +131,9 @@ public APostOperationResponse aPostOperation(APostOperationRequest aPostOperatio .withInput(aPostOperationRequest).withMetricCollector(apiCallMetricCollector) .withMarshaller(new APostOperationRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, aPostOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -178,9 +181,9 @@ public APostOperationWithOutputResponse aPostOperationWithOutput( .withMetricCollector(apiCallMetricCollector) .withMarshaller(new APostOperationWithOutputRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - aPostOperationWithOutputRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationWithOutputRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -228,9 +231,9 @@ public GetWithoutRequiredMembersResponse getWithoutRequiredMembers( .withMetricCollector(apiCallMetricCollector) .withMarshaller(new GetWithoutRequiredMembersRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - getWithoutRequiredMembersRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, + getWithoutRequiredMembersRequest.overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -274,9 +277,9 @@ public PaginatedOperationWithResultKeyResponse paginatedOperationWithResultKey( .withMetricCollector(apiCallMetricCollector) .withMarshaller(new PaginatedOperationWithResultKeyRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - paginatedOperationWithResultKeyRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, + paginatedOperationWithResultKeyRequest.overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -398,9 +401,9 @@ public PaginatedOperationWithoutResultKeyResponse paginatedOperationWithoutResul .withMetricCollector(apiCallMetricCollector) .withMarshaller(new PaginatedOperationWithoutResultKeyRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - paginatedOperationWithoutResultKeyRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, + paginatedOperationWithoutResultKeyRequest.overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -539,9 +542,9 @@ public StreamingInputOperationResponse streamingInputOperation(StreamingInputOpe .delegateMarshaller(new StreamingInputOperationRequestMarshaller(protocolFactory)) .requestBody(requestBody).build())); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingInputOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingInputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -614,9 +617,9 @@ public ReturnT streamingInputOutputOperation( new StreamingInputOutputOperationRequestMarshaller(protocolFactory)) .requestBody(requestBody).transferEncoding(true).build()), responseTransformer); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingInputOutputOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, + streamingInputOutputOperationRequest.overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -667,12 +670,27 @@ public ReturnT streamingOutputOperation(StreamingOutputOperationReques .withMetricCollector(apiCallMetricCollector) .withMarshaller(new StreamingOutputOperationRequestMarshaller(protocolFactory)), responseTransformer); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingOutputOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingOutputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } + private static List resolveMetricPublishers(SdkClientConfiguration clientConfiguration, + RequestOverrideConfiguration requestOverrideConfiguration) { + List publishers = null; + if (requestOverrideConfiguration != null) { + publishers = requestOverrideConfiguration.metricPublishers(); + } + if (publishers == null || publishers.isEmpty()) { + publishers = clientConfiguration.option(SdkClientOption.METRIC_PUBLISHERS); + } + if (publishers == null) { + publishers = Collections.emptyList(); + } + return publishers; + } + private HttpResponseHandler createErrorResponseHandler(BaseAwsJsonProtocolFactory protocolFactory, JsonOperationMetadata operationMetadata) { return protocolFactory.createErrorResponseHandler(operationMetadata); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-async-client-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-async-client-class.java index d17f2d999393..55a72255ad1a 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-async-client-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-async-client-class.java @@ -2,7 +2,8 @@ import static software.amazon.awssdk.utils.FunctionalUtils.runAndLogError; -import java.util.Optional; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,13 +12,14 @@ import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; import software.amazon.awssdk.awscore.client.handler.AwsAsyncClientHandler; import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.async.AsyncResponseTransformer; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.AsyncClientHandler; import software.amazon.awssdk.core.client.handler.ClientExecutionParams; import software.amazon.awssdk.core.http.HttpResponseHandler; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.runtime.transform.AsyncStreamingRequestMarshaller; import software.amazon.awssdk.metrics.MetricCollector; @@ -113,15 +115,14 @@ public CompletableFuture aPostOperation(APostOperationRe .withInput(aPostOperationRequest)); AwsRequestOverrideConfiguration requestOverrideConfig = aPostOperationRequest.overrideConfiguration().orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, aPostOperationRequest + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationRequest .overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -171,15 +172,14 @@ public CompletableFuture aPostOperationWithOut AwsRequestOverrideConfiguration requestOverrideConfig = aPostOperationWithOutputRequest.overrideConfiguration() .orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - aPostOperationWithOutputRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationWithOutputRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -233,15 +233,14 @@ public CompletableFuture streamingInputOperatio AwsRequestOverrideConfiguration requestOverrideConfig = streamingInputOperationRequest.overrideConfiguration() .orElse(null); executeFuture.whenComplete((r, e) -> { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingInputOperationRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingInputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -298,17 +297,16 @@ public CompletableFuture streamingOutputOperation( runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseTransformer.exceptionOccurred(e)); } - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - requestOverrideConfig); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, requestOverrideConfig); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); }); return executeFuture; } catch (Throwable t) { runAndLogError(log, "Exception thrown in exceptionOccurred callback, ignoring", () -> asyncResponseTransformer.exceptionOccurred(t)); - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingOutputOperationRequest.overrideConfiguration().orElse(null)); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingOutputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); return CompletableFutureUtils.failedFuture(t); } } @@ -326,4 +324,20 @@ private AwsQueryProtocolFactory init() { .exceptionBuilderSupplier(InvalidInputException::builder).httpStatusCode(400).build()) .clientConfiguration(clientConfiguration).defaultServiceExceptionSupplier(QueryException::builder).build(); } + + private static List resolveMetricPublishers(SdkClientConfiguration clientConfiguration, + RequestOverrideConfiguration requestOverrideConfiguration) { + List publishers = null; + if (requestOverrideConfiguration != null) { + publishers = requestOverrideConfiguration.metricPublishers(); + } + if (publishers == null || publishers.isEmpty()) { + publishers = clientConfiguration.option(SdkClientOption.METRIC_PUBLISHERS); + } + if (publishers == null) { + publishers = Collections.emptyList(); + } + return publishers; + } } + diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-client-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-client-class.java index 6bf91159f837..28cc96b95475 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-client-class.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-query-client-class.java @@ -1,16 +1,18 @@ package software.amazon.awssdk.services.query; -import java.util.Optional; +import java.util.Collections; +import java.util.List; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler; import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.handler.ClientExecutionParams; import software.amazon.awssdk.core.client.handler.SyncClientHandler; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.http.HttpResponseHandler; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.runtime.transform.StreamingRequestMarshaller; import software.amazon.awssdk.core.sync.RequestBody; @@ -100,8 +102,9 @@ public APostOperationResponse aPostOperation(APostOperationRequest aPostOperatio .withInput(aPostOperationRequest).withMetricCollector(apiCallMetricCollector) .withMarshaller(new APostOperationRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, aPostOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -146,9 +149,9 @@ public APostOperationWithOutputResponse aPostOperationWithOutput( .withMetricCollector(apiCallMetricCollector) .withMarshaller(new APostOperationWithOutputRequestMarshaller(protocolFactory))); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - aPostOperationWithOutputRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, aPostOperationWithOutputRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -205,9 +208,9 @@ public StreamingInputOperationResponse streamingInputOperation(StreamingInputOpe .delegateMarshaller(new StreamingInputOperationRequestMarshaller(protocolFactory)) .requestBody(requestBody).build())); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingInputOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingInputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } @@ -255,12 +258,27 @@ public ReturnT streamingOutputOperation(StreamingOutputOperationReques .withMetricCollector(apiCallMetricCollector) .withMarshaller(new StreamingOutputOperationRequestMarshaller(protocolFactory)), responseTransformer); } finally { - Optional metricPublisher = MetricUtils.resolvePublisher(clientConfiguration, - streamingOutputOperationRequest); - metricPublisher.ifPresent(p -> p.publish(apiCallMetricCollector.collect())); + List metricPublishers = resolveMetricPublishers(clientConfiguration, streamingOutputOperationRequest + .overrideConfiguration().orElse(null)); + metricPublishers.forEach(p -> p.publish(apiCallMetricCollector.collect())); } } + private static List resolveMetricPublishers(SdkClientConfiguration clientConfiguration, + RequestOverrideConfiguration requestOverrideConfiguration) { + List publishers = null; + if (requestOverrideConfiguration != null) { + publishers = requestOverrideConfiguration.metricPublishers(); + } + if (publishers == null || publishers.isEmpty()) { + publishers = clientConfiguration.option(SdkClientOption.METRIC_PUBLISHERS); + } + if (publishers == null) { + publishers = Collections.emptyList(); + } + return publishers; + } + private AwsQueryProtocolFactory init() { return AwsQueryProtocolFactory .builder() @@ -275,3 +293,4 @@ public void close() { clientHandler.close(); } } + diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java index 142a0e062873..0147a7618e2b 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java @@ -43,13 +43,11 @@ import software.amazon.awssdk.core.interceptor.InterceptorContext; import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; -import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.metrics.MetricCollector; import software.amazon.awssdk.utils.IoUtils; -import software.amazon.awssdk.utils.Pair; import software.amazon.awssdk.utils.Validate; import software.amazon.eventstream.HeaderValue; import software.amazon.eventstream.Message; @@ -74,11 +72,11 @@ static ExecutionContext .flatMap(AwsRequestOverrideConfiguration::credentialsProvider) .orElse(clientCredentials); - Pair measuredCredentialsFetch = MetricUtils.measureDuration( - credentialsProvider::resolveCredentials); - AwsCredentials credentials = measuredCredentialsFetch.left(); + long credentialsResolveStart = System.nanoTime(); + AwsCredentials credentials = credentialsProvider.resolveCredentials(); + Duration fetchDuration = Duration.ofNanos(System.nanoTime() - credentialsResolveStart); MetricCollector metricCollector = resolveMetricCollector(executionParams); - metricCollector.reportMetric(CoreMetric.CREDENTIALS_FETCH_DURATION, measuredCredentialsFetch.right()); + metricCollector.reportMetric(CoreMetric.CREDENTIALS_FETCH_DURATION, fetchDuration); Validate.validState(credentials != null, "Credential providers must never return null."); diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java index e60763b964e2..d4c107bef659 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java @@ -45,7 +45,7 @@ public abstract class RequestOverrideConfiguration { private final Duration apiCallTimeout; private final Duration apiCallAttemptTimeout; private final Signer signer; - private final MetricPublisher metricPublisher; + private final List metricPublishers; protected RequestOverrideConfiguration(Builder builder) { this.headers = CollectionUtils.deepUnmodifiableMap(builder.headers(), () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); @@ -54,7 +54,7 @@ protected RequestOverrideConfiguration(Builder builder) { this.apiCallTimeout = Validate.isPositiveOrNull(builder.apiCallTimeout(), "apiCallTimeout"); this.apiCallAttemptTimeout = Validate.isPositiveOrNull(builder.apiCallAttemptTimeout(), "apiCallAttemptTimeout"); this.signer = builder.signer(); - this.metricPublisher = builder.metricPublisher(); + this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers())); } /** @@ -131,11 +131,11 @@ public Optional signer() { } /** - * Return the metric publisher for publishing the metrics collected for this request. This publisher supersedes the - * metric publisher set on the client. + * Return the metric publishers for publishing the metrics collected for this request. This list supersedes the + * metric publishers set on the client. */ - public Optional metricPublisher() { - return Optional.ofNullable(metricPublisher); + public List metricPublishers() { + return metricPublishers; } @Override @@ -153,7 +153,7 @@ public boolean equals(Object o) { Objects.equals(apiCallTimeout, that.apiCallTimeout) && Objects.equals(apiCallAttemptTimeout, that.apiCallAttemptTimeout) && Objects.equals(signer, that.signer) && - Objects.equals(metricPublisher, that.metricPublisher); + Objects.equals(metricPublishers, that.metricPublishers); } @Override @@ -165,7 +165,7 @@ public int hashCode() { hashCode = 31 * hashCode + Objects.hashCode(apiCallTimeout); hashCode = 31 * hashCode + Objects.hashCode(apiCallAttemptTimeout); hashCode = 31 * hashCode + Objects.hashCode(signer); - hashCode = 31 * hashCode + Objects.hashCode(metricPublisher); + hashCode = 31 * hashCode + Objects.hashCode(metricPublishers); return hashCode; } @@ -353,15 +353,17 @@ default B putRawQueryParameter(String name, String value) { Signer signer(); /** - * Sets the metric publisher for publishing the metrics collected for this request. This publisher supersedes + * Sets the metric publishers for publishing the metrics collected for this request. This list supersedes * the metric publisher set on the client. * - * @param metricPublisher The metric publisher for this request. + * @param metricPublisher The list metric publisher for this request. * @return This object for method chaining. */ - B metricPublisher(MetricPublisher metricPublisher); + B metricPublishers(List metricPublisher); - MetricPublisher metricPublisher(); + B addMetricPublisher(MetricPublisher metricPublisher); + + List metricPublishers(); /** * Create a new {@code SdkRequestOverrideConfiguration} with the properties set on this builder. @@ -378,7 +380,7 @@ protected abstract static class BuilderImpl implements Builde private Duration apiCallTimeout; private Duration apiCallAttemptTimeout; private Signer signer; - private MetricPublisher metricPublisher; + private List metricPublishers = new ArrayList<>(); protected BuilderImpl() { } @@ -497,18 +499,26 @@ public Signer signer() { } @Override - public B metricPublisher(MetricPublisher metricPublisher) { - this.metricPublisher = metricPublisher; + public B metricPublishers(List metricPublishers) { + Validate.paramNotNull(metricPublishers, "metricPublishers"); + this.metricPublishers = new ArrayList<>(metricPublishers); + return (B) this; + } + + @Override + public B addMetricPublisher(MetricPublisher metricPublisher) { + Validate.paramNotNull(metricPublisher, "metricPublisher"); + this.metricPublishers.add(metricPublisher); return (B) this; } - public void setMetricPublisher(MetricPublisher metricPublisher) { - metricPublisher(metricPublisher); + public void setMetricPublishers(List metricPublishers) { + metricPublishers(metricPublishers); } @Override - public MetricPublisher metricPublisher() { - return metricPublisher; + public List metricPublishers() { + return metricPublishers; } } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java index 4dde45af566c..6725771e737b 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java @@ -28,7 +28,7 @@ import static software.amazon.awssdk.core.client.config.SdkClientOption.ASYNC_HTTP_CLIENT; import static software.amazon.awssdk.core.client.config.SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED; import static software.amazon.awssdk.core.client.config.SdkClientOption.EXECUTION_INTERCEPTORS; -import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHER; +import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHERS; import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE; import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_NAME; import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY; @@ -366,7 +366,7 @@ public final B overrideConfiguration(ClientOverrideConfiguration overrideConfig) overrideConfig.advancedOption(DISABLE_HOST_PREFIX_INJECTION).orElse(null)); clientConfiguration.option(PROFILE_FILE, overrideConfig.defaultProfileFile().orElse(null)); clientConfiguration.option(PROFILE_NAME, overrideConfig.defaultProfileName().orElse(null)); - clientConfiguration.option(METRIC_PUBLISHER, overrideConfig.metricPublisher().orElse(null)); + clientConfiguration.option(METRIC_PUBLISHERS, overrideConfig.metricPublishers()); return thisBuilder(); } @@ -394,8 +394,8 @@ public final B httpClientBuilder(SdkAsyncHttpClient.Builder httpClientBuilder) { return thisBuilder(); } - public final B metricPublisher(MetricPublisher metricPublisher) { - clientConfiguration.option(METRIC_PUBLISHER, metricPublisher); + public final B metricPublishers(List metricPublishers) { + clientConfiguration.option(METRIC_PUBLISHERS, metricPublishers); return thisBuilder(); } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java index ec70e54e1892..30641d2a5b36 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java @@ -56,7 +56,7 @@ public final class ClientOverrideConfiguration private final Duration apiCallTimeout; private final ProfileFile defaultProfileFile; private final String defaultProfileName; - private final MetricPublisher metricPublisher; + private final List metricPublishers; /** * Initialize this configuration. Private to require use of {@link #builder()}. @@ -70,7 +70,7 @@ private ClientOverrideConfiguration(Builder builder) { this.apiCallAttemptTimeout = Validate.isPositiveOrNull(builder.apiCallAttemptTimeout(), "apiCallAttemptTimeout"); this.defaultProfileFile = builder.defaultProfileFile(); this.defaultProfileName = builder.defaultProfileName(); - this.metricPublisher = builder.metricPublisher(); + this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers())); } @Override @@ -188,12 +188,12 @@ public Optional defaultProfileName() { } /** - * The metric publisher to use to publisher metrics collected for this client. + * The metric publishers to use to publisher metrics collected for this client. * * @return The metric publisher. */ - public Optional metricPublisher() { - return Optional.ofNullable(metricPublisher); + public List metricPublishers() { + return metricPublishers; } @Override @@ -421,15 +421,12 @@ default Builder retryPolicy(RetryMode retryMode) { String defaultProfileName(); - /** - * Set the metric publisher to use for publishing metrics collected fo this client. - * - * @param metricPublisher The metric publisher to use. - * @return This object for method chaining. - */ - Builder metricPublisher(MetricPublisher metricPublisher); - MetricPublisher metricPublisher(); + Builder metricPublishers(List metricPublishers); + + Builder addMetricPublisher(MetricPublisher metricPublisher); + + List metricPublishers(); } /** @@ -444,7 +441,7 @@ private static final class DefaultClientOverrideConfigurationBuilder implements private Duration apiCallAttemptTimeout; private ProfileFile defaultProfileFile; private String defaultProfileName; - private MetricPublisher metricPublisher; + private List metricPublishers = new ArrayList<>(); @Override public Builder headers(Map> headers) { @@ -587,14 +584,26 @@ public Builder defaultProfileName(String defaultProfileName) { } @Override - public Builder metricPublisher(MetricPublisher metricPublisher) { - this.metricPublisher = metricPublisher; + public Builder metricPublishers(List metricPublishers) { + Validate.paramNotNull(metricPublishers, "metricPublishers"); + this.metricPublishers = new ArrayList<>(metricPublishers); + return this; + } + + public void setMetricPublishers(List metricPublishers) { + metricPublishers(metricPublishers); + } + + @Override + public Builder addMetricPublisher(MetricPublisher metricPublisher) { + Validate.paramNotNull(metricPublisher, "metricPublisher"); + this.metricPublishers.add(metricPublisher); return this; } @Override - public MetricPublisher metricPublisher() { - return metricPublisher; + public List metricPublishers() { + return Collections.unmodifiableList(metricPublishers); } @Override diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java index 98669b52dc2c..ad422d83070e 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java @@ -130,7 +130,8 @@ public final class SdkClientOption extends ClientOption { */ public static final SdkClientOption PROFILE_NAME = new SdkClientOption<>(String.class); - public static final SdkClientOption METRIC_PUBLISHER = new SdkClientOption<>(MetricPublisher.class); + public static final SdkClientOption> METRIC_PUBLISHERS = + new SdkClientOption<>(new UnsafeValueType(List.class)); private SdkClientOption(Class valueClass) { super(valueClass); diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallMetricCollectionStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallMetricCollectionStage.java index 96ccdcebd133..3d57cedea52d 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallMetricCollectionStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallMetricCollectionStage.java @@ -23,6 +23,7 @@ import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.metrics.MetricCollector; +import software.amazon.awssdk.utils.CompletableFutureUtils; /** * Wrapper pipeline that tracks the {@link CoreMetric#API_CALL_DURATION} metric. @@ -40,14 +41,22 @@ public AsyncApiCallMetricCollectionStage(RequestPipeline execute(SdkHttpFullRequest input, RequestExecutionContext context) throws Exception { MetricCollector metricCollector = context.executionContext().metricCollector(); + CompletableFuture future = new CompletableFuture<>(); + long callStart = System.nanoTime(); - CompletableFuture future = wrapped.execute(input, context); + CompletableFuture executeFuture = wrapped.execute(input, context); - future.whenComplete((r, t) -> { + executeFuture.whenComplete((r, t) -> { long duration = System.nanoTime() - callStart; metricCollector.reportMetric(CoreMetric.API_CALL_DURATION, Duration.ofNanos(duration)); + + if (t != null) { + future.completeExceptionally(t); + } else { + future.complete(r); + } }); - return future; + return CompletableFutureUtils.forwardExceptionTo(future, executeFuture); } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/MetricUtils.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/MetricUtils.java index a4e1fb26a3eb..77cab23ce4fd 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/MetricUtils.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/MetricUtils.java @@ -15,24 +15,17 @@ package software.amazon.awssdk.core.internal.util; -import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHER; import static software.amazon.awssdk.core.http.HttpResponseHandler.X_AMZN_REQUEST_ID_HEADER; import java.time.Duration; -import java.util.Optional; import java.util.concurrent.Callable; import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.RequestOverrideConfiguration; -import software.amazon.awssdk.core.SdkRequest; -import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.internal.http.RequestExecutionContext; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.http.SdkHttpFullResponse; import software.amazon.awssdk.metrics.MetricCollector; -import software.amazon.awssdk.metrics.MetricPublisher; import software.amazon.awssdk.metrics.NoOpMetricCollector; -import software.amazon.awssdk.utils.OptionalUtils; import software.amazon.awssdk.utils.Pair; /** @@ -44,40 +37,6 @@ public final class MetricUtils { private MetricUtils() { } - /** - * Resolve the correct metric publisher to use. The publisher set on the request always takes precedence. - * - * @param clientConfig The client configuration. - * @param requestConfig The request override configuration. - * @return The metric publisher to use. - */ - //TODO: remove this and use the overload instead - public static Optional resolvePublisher(SdkClientConfiguration clientConfig, - SdkRequest requestConfig) { - Optional requestOverride = requestConfig.overrideConfiguration() - .flatMap(RequestOverrideConfiguration::metricPublisher); - if (requestOverride.isPresent()) { - return requestOverride; - } - return Optional.ofNullable(clientConfig.option(METRIC_PUBLISHER)); - } - - /** - * Resolve the correct metric publisher to use. The publisher set on the request always takes precedence. - * - * @param clientConfig The client configuration. - * @param requestConfig The request override configuration. - * @return The metric publisher to use. - */ - public static Optional resolvePublisher(SdkClientConfiguration clientConfig, - RequestOverrideConfiguration requestConfig) { - if (requestConfig != null) { - return OptionalUtils.firstPresent(requestConfig.metricPublisher(), () -> clientConfig.option(METRIC_PUBLISHER)); - } - - return Optional.ofNullable(clientConfig.option(METRIC_PUBLISHER)); - } - /** * Measure the duration of the given callable. * diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/RequestOverrideConfigurationTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/RequestOverrideConfigurationTest.java index 4ae3e660a470..dd275b54f304 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/RequestOverrideConfigurationTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/RequestOverrideConfigurationTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.Arrays; @@ -25,6 +26,7 @@ import java.util.List; import java.util.Map; import org.junit.Test; +import software.amazon.awssdk.metrics.MetricPublisher; import software.amazon.awssdk.utils.ImmutableMap; public class RequestOverrideConfigurationTest { @@ -93,4 +95,74 @@ public void shouldGuaranteeImmutability() { assertThat(configurationBuilder.headers().size()).isEqualTo(1); assertThat(configurationBuilder.headers().get("foo")).containsExactly("bar"); } + + @Test + public void metricPublishers_createsCopy() { + List publishers = new ArrayList<>(); + publishers.add(mock(MetricPublisher.class)); + List toModify = new ArrayList<>(publishers); + + SdkRequestOverrideConfiguration overrideConfig = SdkRequestOverrideConfiguration.builder() + .metricPublishers(toModify) + .build(); + + toModify.clear(); + + assertThat(overrideConfig.metricPublishers()).isEqualTo(publishers); + } + + @Test + public void addMetricPublisher_maintainsAllAdded() { + List publishers = new ArrayList<>(); + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + + SdkRequestOverrideConfiguration.Builder builder = SdkRequestOverrideConfiguration.builder(); + + publishers.forEach(builder::addMetricPublisher); + + SdkRequestOverrideConfiguration overrideConfig = builder.build(); + + assertThat(overrideConfig.metricPublishers()).isEqualTo(publishers); + } + + @Test + public void metricPublishers_overwritesPreviouslyAdded() { + MetricPublisher firstAdded = mock(MetricPublisher.class); + + List publishers = new ArrayList<>(); + + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + + SdkRequestOverrideConfiguration.Builder builder = SdkRequestOverrideConfiguration.builder(); + + builder.addMetricPublisher(firstAdded); + + builder.metricPublishers(publishers); + + SdkRequestOverrideConfiguration overrideConfig = builder.build(); + + assertThat(overrideConfig.metricPublishers()).isEqualTo(publishers); + } + + @Test + public void addMetricPublisher_listPreviouslyAdded_appendedToList() { + List publishers = new ArrayList<>(); + + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + + MetricPublisher thirdAdded = mock(MetricPublisher.class); + + SdkRequestOverrideConfiguration.Builder builder = SdkRequestOverrideConfiguration.builder(); + + builder.metricPublishers(publishers); + builder.addMetricPublisher(thirdAdded); + + SdkRequestOverrideConfiguration overrideConfig = builder.build(); + + assertThat(overrideConfig.metricPublishers()).containsExactly(publishers.get(0), publishers.get(1), thirdAdded); + } } diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/config/ClientOverrideConfigurationTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/config/ClientOverrideConfigurationTest.java index a24136c60460..f4a0e1da7701 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/config/ClientOverrideConfigurationTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/config/ClientOverrideConfigurationTest.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.Arrays; @@ -28,6 +29,7 @@ import org.junit.Test; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.internal.http.request.SlowExecutionInterceptor; +import software.amazon.awssdk.metrics.MetricPublisher; import software.amazon.awssdk.utils.ImmutableMap; public class ClientOverrideConfigurationTest { @@ -108,4 +110,74 @@ public void shouldGuaranteeImmutability() { assertThat(configurationBuilder.headers().get("foo")).containsExactly("bar"); assertThat(configurationBuilder.executionInterceptors()).containsExactly(slowExecutionInterceptor); } + + @Test + public void metricPublishers_createsCopy() { + List publishers = new ArrayList<>(); + publishers.add(mock(MetricPublisher.class)); + List toModify = new ArrayList<>(publishers); + + ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() + .metricPublishers(toModify) + .build(); + + toModify.clear(); + + assertThat(overrideConfig.metricPublishers()).isEqualTo(publishers); + } + + @Test + public void addMetricPublisher_maintainsAllAdded() { + List publishers = new ArrayList<>(); + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + + ClientOverrideConfiguration.Builder builder = ClientOverrideConfiguration.builder(); + + publishers.forEach(builder::addMetricPublisher); + + ClientOverrideConfiguration overrideConfig = builder.build(); + + assertThat(overrideConfig.metricPublishers()).isEqualTo(publishers); + } + + @Test + public void metricPublishers_overwritesPreviouslyAdded() { + MetricPublisher firstAdded = mock(MetricPublisher.class); + + List publishers = new ArrayList<>(); + + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + + ClientOverrideConfiguration.Builder builder = ClientOverrideConfiguration.builder(); + + builder.addMetricPublisher(firstAdded); + + builder.metricPublishers(publishers); + + ClientOverrideConfiguration overrideConfig = builder.build(); + + assertThat(overrideConfig.metricPublishers()).isEqualTo(publishers); + } + + @Test + public void addMetricPublisher_listPreviouslyAdded_appendedToList() { + List publishers = new ArrayList<>(); + + publishers.add(mock(MetricPublisher.class)); + publishers.add(mock(MetricPublisher.class)); + + MetricPublisher thirdAdded = mock(MetricPublisher.class); + + ClientOverrideConfiguration.Builder builder = ClientOverrideConfiguration.builder(); + + builder.metricPublishers(publishers); + builder.addMetricPublisher(thirdAdded); + + ClientOverrideConfiguration overrideConfig = builder.build(); + + assertThat(overrideConfig.metricPublishers()).containsExactly(publishers.get(0), publishers.get(1), thirdAdded); + } } diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/timers/AsyncHttpClientApiCallTimeoutTests.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/timers/AsyncHttpClientApiCallTimeoutTests.java index b4d5ad181976..50c47c953bee 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/timers/AsyncHttpClientApiCallTimeoutTests.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/timers/AsyncHttpClientApiCallTimeoutTests.java @@ -108,6 +108,7 @@ public void errorResponse_SlowAfterErrorRequestHandler_ThrowsApiCallTimeoutExcep .interceptorChain(interceptors) .executionAttributes(new ExecutionAttributes()) .interceptorContext(incerceptorContext) + .metricCollector(MetricCollector.create("ApiCall")) .build(); CompletableFuture future = diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MetricUtilsTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MetricUtilsTest.java index c97696856c04..9fead321d00a 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MetricUtilsTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/util/MetricUtilsTest.java @@ -18,24 +18,16 @@ import static org.assertj.core.api.Java6Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHER; import java.io.IOException; import java.time.Duration; -import java.util.Optional; -import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import software.amazon.awssdk.core.RequestOverrideConfiguration; -import software.amazon.awssdk.core.SdkRequestOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkClientConfiguration; -import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.http.HttpResponseHandler; import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.http.SdkHttpFullResponse; import software.amazon.awssdk.metrics.MetricCollector; -import software.amazon.awssdk.metrics.MetricPublisher; import software.amazon.awssdk.utils.Pair; public class MetricUtilsTest { @@ -121,24 +113,4 @@ public void testCollectHttpMetrics_collectsAllExpectedMetrics() { verify(mockCollector).reportMetric(CoreMetric.AWS_EXTENDED_REQUEST_ID, requestId2); } - - @Test - public void resolvePublisher_requestConfigNull_ShouldUseSdkClientConfig() { - MetricPublisher metricPublisher = mock(MetricPublisher.class); - - SdkClientConfiguration config = SdkClientConfiguration.builder().option(METRIC_PUBLISHER, metricPublisher).build(); - RequestOverrideConfiguration requestOverrideConfiguration = null; - Optional result = MetricUtils.resolvePublisher(config, requestOverrideConfiguration); - Assertions.assertThat(result).isEqualTo(Optional.of(metricPublisher)); - } - - @Test - public void resolvePublisher_requestConfigNotNull_shouldTakePrecedence() { - MetricPublisher metricPublisher = mock(MetricPublisher.class); - - SdkClientConfiguration config = SdkClientConfiguration.builder().option(METRIC_PUBLISHER, mock(MetricPublisher.class)).build(); - RequestOverrideConfiguration requestOverrideConfiguration = SdkRequestOverrideConfiguration.builder().metricPublisher(metricPublisher).build(); - Optional result = MetricUtils.resolvePublisher(config, requestOverrideConfiguration); - Assertions.assertThat(result).isEqualTo(Optional.of(metricPublisher)); - } } diff --git a/services/transcribestreaming/src/it/java/software/amazon/awssdk/services/transcribestreaming/TranscribeStreamingIntegrationTest.java b/services/transcribestreaming/src/it/java/software/amazon/awssdk/services/transcribestreaming/TranscribeStreamingIntegrationTest.java index 94a94e9879ea..4ba696d8f35a 100644 --- a/services/transcribestreaming/src/it/java/software/amazon/awssdk/services/transcribestreaming/TranscribeStreamingIntegrationTest.java +++ b/services/transcribestreaming/src/it/java/software/amazon/awssdk/services/transcribestreaming/TranscribeStreamingIntegrationTest.java @@ -70,7 +70,7 @@ public static void setup() { client = TranscribeStreamingAsyncClient.builder() .region(Region.US_EAST_1) .overrideConfiguration(b -> b.addExecutionInterceptor(new VerifyHeaderInterceptor()) - .metricPublisher(mockPublisher)) + .addMetricPublisher(mockPublisher)) .credentialsProvider(getCredentials()) .build(); } @@ -160,7 +160,6 @@ private void verifyMetrics() throws InterruptedException { MetricCollection attemptCollection = capturedCollection.children().get(0); assertThat(attemptCollection.name()).isEqualTo("ApiCallAttempt"); - assertThat(attemptCollection.children()).isEmpty(); assertThat(attemptCollection.metricValues(CoreMetric.HTTP_STATUS_CODE)) .containsExactly(200); assertThat(attemptCollection.metricValues(CoreMetric.SIGNING_DURATION).get(0)) diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/CoreMetricsTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/CoreMetricsTest.java index bc6298ffd760..a24c81fb01cd 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/CoreMetricsTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/CoreMetricsTest.java @@ -74,7 +74,7 @@ public void setup() throws IOException { client = ProtocolRestJsonClient.builder() .httpClient(mockHttpClient) .credentialsProvider(mockCredentialsProvider) - .overrideConfiguration(c -> c.metricPublisher(mockPublisher).retryPolicy(b -> b.numRetries(MAX_RETRIES))) + .overrideConfiguration(c -> c.addMetricPublisher(mockPublisher).retryPolicy(b -> b.numRetries(MAX_RETRIES))) .build(); AbortableInputStream content = contentStream("{}"); SdkHttpFullResponse httpResponse = SdkHttpFullResponse.builder() @@ -130,7 +130,7 @@ public void testApiCall_noConfiguredPublisher_succeeds() { public void testApiCall_publisherOverriddenOnRequest_requestPublisherTakesPrecedence() { MetricPublisher requestMetricPublisher = mock(MetricPublisher.class); - client.allTypes(r -> r.overrideConfiguration(o -> o.metricPublisher(requestMetricPublisher))); + client.allTypes(r -> r.overrideConfiguration(o -> o.addMetricPublisher(requestMetricPublisher))); verify(requestMetricPublisher).publish(any(MetricCollection.class)); verifyZeroInteractions(mockPublisher); diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/SyncClientMetricPublisherResolutionTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/SyncClientMetricPublisherResolutionTest.java new file mode 100644 index 000000000000..9006ec7d6c44 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/SyncClientMetricPublisherResolutionTest.java @@ -0,0 +1,176 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services.metrics; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.http.ExecutableHttpRequest; +import software.amazon.awssdk.http.HttpExecuteRequest; +import software.amazon.awssdk.http.HttpExecuteResponse; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.metrics.MetricCollection; +import software.amazon.awssdk.metrics.MetricPublisher; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClientBuilder; + +@RunWith(MockitoJUnitRunner.class) +public class SyncClientMetricPublisherResolutionTest { + + @Mock + private SdkHttpClient mockHttpClient; + + @Mock + private AwsCredentialsProvider mockCredentialsProvider; + + private ProtocolRestJsonClient client; + + @After + public void teardown() { + if (client != null) { + client.close(); + } + + client = null; + } + + @Test + public void testApiCall_noPublishersSet_noException() throws IOException { + client = clientWithPublishers(); + client.allTypes(); + } + + @Test + public void testApiCall_publishersSetOnClient_clientPublishersInvoked() throws IOException { + MetricPublisher publisher1 = mock(MetricPublisher.class); + MetricPublisher publisher2 = mock(MetricPublisher.class); + + client = clientWithPublishers(publisher1, publisher2); + + try { + client.allTypes(); + } catch (Throwable t) { + // ignored, call fails because our mock HTTP client isn't set up + } finally { + verify(publisher1).publish(any(MetricCollection.class)); + verify(publisher2).publish(any(MetricCollection.class)); + } + } + + @Test + public void testApiCall_publishersSetOnRequest_requestPublishersInvoked() throws IOException { + MetricPublisher publisher1 = mock(MetricPublisher.class); + MetricPublisher publisher2 = mock(MetricPublisher.class); + + client = clientWithPublishers(); + + try { + client.allTypes(r -> r.overrideConfiguration(o -> + o.addMetricPublisher(publisher1).addMetricPublisher(publisher2))); + } catch (Throwable t) { + // ignored, call fails because our mock HTTP client isn't set up + } finally { + verify(publisher1).publish(any(MetricCollection.class)); + verify(publisher2).publish(any(MetricCollection.class)); + } + } + + @Test + public void testApiCall_publishersSetOnClientAndRequest_requestPublishersInvoked() throws IOException { + MetricPublisher clientPublisher1 = mock(MetricPublisher.class); + MetricPublisher clientPublisher2 = mock(MetricPublisher.class); + + MetricPublisher requestPublisher1 = mock(MetricPublisher.class); + MetricPublisher requestPublisher2 = mock(MetricPublisher.class); + + client = clientWithPublishers(clientPublisher1, clientPublisher2); + + try { + client.allTypes(r -> r.overrideConfiguration(o -> + o.addMetricPublisher(requestPublisher1).addMetricPublisher(requestPublisher2))); + } catch (Throwable t) { + // ignored, call fails because our mock HTTP client isn't set up + } finally { + verify(requestPublisher1).publish(any(MetricCollection.class)); + verify(requestPublisher2).publish(any(MetricCollection.class)); + verifyZeroInteractions(clientPublisher1); + verifyZeroInteractions(clientPublisher2); + } + } + + private ProtocolRestJsonClient clientWithPublishers(MetricPublisher... metricPublishers) throws IOException { + ProtocolRestJsonClientBuilder builder = ProtocolRestJsonClient.builder() + .httpClient(mockHttpClient) + .credentialsProvider(mockCredentialsProvider); + + AbortableInputStream content = AbortableInputStream.create(new ByteArrayInputStream("{}".getBytes())); + SdkHttpFullResponse httpResponse = SdkHttpFullResponse.builder() + .statusCode(200) + .content(content) + .build(); + + HttpExecuteResponse mockResponse = mockExecuteResponse(httpResponse); + + ExecutableHttpRequest mockExecuteRequest = mock(ExecutableHttpRequest.class); + when(mockExecuteRequest.call()).thenAnswer(invocation -> { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + return mockResponse; + }); + + when(mockHttpClient.prepareRequest(any(HttpExecuteRequest.class))) + .thenReturn(mockExecuteRequest); + + when(mockCredentialsProvider.resolveCredentials()).thenAnswer(invocation -> { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + return AwsBasicCredentials.create("foo", "bar"); + }); + + if (metricPublishers != null) { + builder.overrideConfiguration(o -> o.metricPublishers(Arrays.asList(metricPublishers))); + } + + return builder.build(); + } + + private static HttpExecuteResponse mockExecuteResponse(SdkHttpFullResponse httpResponse) { + HttpExecuteResponse mockResponse = mock(HttpExecuteResponse.class); + when(mockResponse.httpResponse()).thenReturn(httpResponse); + when(mockResponse.responseBody()).thenReturn(httpResponse.content()); + return mockResponse; + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncClientMetricPublisherResolutionTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncClientMetricPublisherResolutionTest.java new file mode 100644 index 000000000000..64ea187f46f8 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncClientMetricPublisherResolutionTest.java @@ -0,0 +1,160 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services.metrics.async; + +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import java.io.IOException; +import java.net.URI; +import java.util.Arrays; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.metrics.MetricCollection; +import software.amazon.awssdk.metrics.MetricPublisher; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClientBuilder; +import software.amazon.awssdk.services.protocolrestjson.model.ProtocolRestJsonException; + +@RunWith(MockitoJUnitRunner.class) +public class AsyncClientMetricPublisherResolutionTest { + @Mock + private AwsCredentialsProvider mockCredentialsProvider; + + @Rule + public WireMockRule wireMock = new WireMockRule(0); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private ProtocolRestJsonAsyncClient client; + + + @Before + public void setup() { + when(mockCredentialsProvider.resolveCredentials()).thenAnswer(invocation -> { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + return AwsBasicCredentials.create("foo", "bar"); + }); + } + + @After + public void teardown() { + wireMock.resetAll(); + if (client != null) { + client.close(); + } + client = null; + } + + @Test + public void testApiCall_noPublishersSet_noNpe() { + client = clientWithPublishers(); + // This is thrown because all the requests to our wiremock are + // nonsense, it's just important that we don't get NPE because we + // don't have publishers set + thrown.expectCause(instanceOf(ProtocolRestJsonException.class)); + client.allTypes().join(); + } + + @Test + public void testApiCall_publishersSetOnClient_clientPublishersInvoked() throws IOException { + MetricPublisher publisher1 = mock(MetricPublisher.class); + MetricPublisher publisher2 = mock(MetricPublisher.class); + + client = clientWithPublishers(publisher1, publisher2); + + try { + client.allTypes().join(); + } catch (Throwable t) { + // ignored, call fails because our mock HTTP client isn't set up + } finally { + verify(publisher1).publish(any(MetricCollection.class)); + verify(publisher2).publish(any(MetricCollection.class)); + } + } + + @Test + public void testApiCall_publishersSetOnRequest_requestPublishersInvoked() throws IOException { + MetricPublisher publisher1 = mock(MetricPublisher.class); + MetricPublisher publisher2 = mock(MetricPublisher.class); + + client = clientWithPublishers(); + + try { + client.allTypes(r -> r.overrideConfiguration(o -> + o.addMetricPublisher(publisher1).addMetricPublisher(publisher2))) + .join(); + } catch (Throwable t) { + // ignored, call fails because our mock HTTP client isn't set up + } finally { + verify(publisher1).publish(any(MetricCollection.class)); + verify(publisher2).publish(any(MetricCollection.class)); + } + } + + @Test + public void testApiCall_publishersSetOnClientAndRequest_requestPublishersInvoked() throws IOException { + MetricPublisher clientPublisher1 = mock(MetricPublisher.class); + MetricPublisher clientPublisher2 = mock(MetricPublisher.class); + + MetricPublisher requestPublisher1 = mock(MetricPublisher.class); + MetricPublisher requestPublisher2 = mock(MetricPublisher.class); + + client = clientWithPublishers(clientPublisher1, clientPublisher2); + + try { + client.allTypes(r -> r.overrideConfiguration(o -> + o.addMetricPublisher(requestPublisher1).addMetricPublisher(requestPublisher2))) + .join(); + } catch (Throwable t) { + // ignored, call fails because our mock HTTP client isn't set up + } finally { + verify(requestPublisher1).publish(any(MetricCollection.class)); + verify(requestPublisher2).publish(any(MetricCollection.class)); + verifyZeroInteractions(clientPublisher1); + verifyZeroInteractions(clientPublisher2); + } + } + + private ProtocolRestJsonAsyncClient clientWithPublishers(MetricPublisher... metricPublishers) { + ProtocolRestJsonAsyncClientBuilder builder = ProtocolRestJsonAsyncClient.builder() + .credentialsProvider(mockCredentialsProvider) + .endpointOverride(URI.create("http://localhost:" + wireMock.port())); + + if (metricPublishers != null) { + builder.overrideConfiguration(o -> o.metricPublishers(Arrays.asList(metricPublishers))); + } + + return builder.build(); + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncCoreMetricsTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncCoreMetricsTest.java index cdf02eeccdc1..438d3658d1af 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncCoreMetricsTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncCoreMetricsTest.java @@ -62,7 +62,7 @@ public void setup() throws IOException { client = ProtocolRestJsonAsyncClient.builder() .credentialsProvider(mockCredentialsProvider) .endpointOverride(URI.create("http://localhost:" + wireMock.port())) - .overrideConfiguration(c -> c.metricPublisher(mockPublisher).retryPolicy(b -> b.numRetries(MAX_RETRIES))) + .overrideConfiguration(c -> c.addMetricPublisher(mockPublisher).retryPolicy(b -> b.numRetries(MAX_RETRIES))) .build(); when(mockCredentialsProvider.resolveCredentials()).thenAnswer(invocation -> { @@ -114,7 +114,7 @@ public void apiCall_publisherOverriddenOnRequest_requestPublisherTakesPrecedence stubSuccessfulResponse(); MetricPublisher requestMetricPublisher = mock(MetricPublisher.class); - client.allTypes(r -> r.overrideConfiguration(o -> o.metricPublisher(requestMetricPublisher))).join(); + client.allTypes(r -> r.overrideConfiguration(o -> o.addMetricPublisher(requestMetricPublisher))).join(); verify(requestMetricPublisher).publish(any(MetricCollection.class)); verifyZeroInteractions(mockPublisher); diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncEventStreamingCoreMetricsTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncEventStreamingCoreMetricsTest.java index 26dcd794ec09..8641e9db4e39 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncEventStreamingCoreMetricsTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncEventStreamingCoreMetricsTest.java @@ -58,7 +58,7 @@ public void setup() { client = ProtocolRestJsonAsyncClient.builder() .credentialsProvider(mockCredentialsProvider) .endpointOverride(URI.create("http://localhost:" + wireMock.port())) - .overrideConfiguration(c -> c.metricPublisher(mockPublisher) + .overrideConfiguration(c -> c.addMetricPublisher(mockPublisher) .retryPolicy(b -> b.numRetries(MAX_RETRIES))) .build(); @@ -99,12 +99,4 @@ Supplier> callable() { MetricPublisher publisher() { return mockPublisher; } - - void addDelayIfNeeded() { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } } diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncStreamingCoreMetricsTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncStreamingCoreMetricsTest.java index 01a591741dda..5b6b148046f1 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncStreamingCoreMetricsTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/AsyncStreamingCoreMetricsTest.java @@ -57,7 +57,7 @@ public void setup() throws IOException { client = ProtocolRestJsonAsyncClient.builder() .credentialsProvider(mockCredentialsProvider) .endpointOverride(URI.create("http://localhost:" + wireMock.port())) - .overrideConfiguration(c -> c.metricPublisher(mockPublisher).retryPolicy(b -> b.numRetries(MAX_RETRIES))) + .overrideConfiguration(c -> c.addMetricPublisher(mockPublisher).retryPolicy(b -> b.numRetries(MAX_RETRIES))) .build(); when(mockCredentialsProvider.resolveCredentials()).thenAnswer(invocation -> { diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/BaseAsyncCoreMetricsTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/BaseAsyncCoreMetricsTest.java index 865819fdbf42..216477f33784 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/BaseAsyncCoreMetricsTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/BaseAsyncCoreMetricsTest.java @@ -134,10 +134,13 @@ public void apiCall_firstAttemptFailedRetrySucceeded() { /** * Adds delay after calling CompletableFuture.join to wait for publisher to get metrics. - * no op by default, can be overridden by subclasses */ void addDelayIfNeeded() { - // no op by default + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } } abstract String operationName();