From 61268cfd946a106d33436b855726d030db0868a2 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 14 Jun 2021 17:28:47 +0300 Subject: [PATCH] Add UrlTemplatePath handling for Reactive REST Client --- .../JaxrsClientReactiveProcessor.java | 28 +++++++++++++++++-- .../handlers/ClientObservabilityHandler.java | 22 +++++++++++++++ .../reactive/client/impl/HandlerChain.java | 13 ++++++++- .../client/impl/RestClientRequestContext.java | 4 +++ .../reactive/client/impl/WebTargetImpl.java | 18 ++++++++++-- 5 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientObservabilityHandler.java diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index 371e9b24ef7bd8..a11199347a4fea 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -51,10 +51,12 @@ import org.jboss.jandex.PrimitiveType; import org.jboss.jandex.Type; import org.jboss.logging.Logger; +import org.jboss.resteasy.reactive.client.handlers.ClientObservabilityHandler; import org.jboss.resteasy.reactive.client.impl.AsyncInvokerImpl; import org.jboss.resteasy.reactive.client.impl.ClientImpl; import org.jboss.resteasy.reactive.client.impl.UniInvoker; import org.jboss.resteasy.reactive.client.impl.WebTargetImpl; +import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.core.GenericTypeMapping; import org.jboss.resteasy.reactive.common.core.ResponseBuilderFactory; import org.jboss.resteasy.reactive.common.core.Serialisers; @@ -78,6 +80,8 @@ import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.MethodDescriptors; import io.quarkus.arc.processor.Types; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.GeneratedClassGizmoAdaptor; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -90,6 +94,7 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; +import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.recording.RecorderContext; import io.quarkus.deployment.util.JandexUtil; import io.quarkus.gizmo.AssignableResultHandle; @@ -121,6 +126,7 @@ import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterOverrideBuildItem; import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.metrics.MetricsFactory; import io.smallrye.mutiny.Uni; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.multipart.MultipartForm; @@ -171,6 +177,7 @@ void setupClientProxies(JaxrsClientReactiveRecorder recorder, List enricherBuildItems, BeanArchiveIndexBuildItem beanArchiveIndexBuildItem, Optional resourceScanningResultBuildItem, + Capabilities capabilities, Optional metricsCapability, ResteasyReactiveConfig config, RecorderContext recorderContext, BuildProducer generatedClassBuildItemBuildProducer, @@ -215,6 +222,10 @@ void setupClientProxies(JaxrsClientReactiveRecorder recorder, .setSmartDefaultProduces(disableSmartDefaultProduces.isEmpty()) .build(); + boolean observabilityIntegrationNeeded = (capabilities.isPresent(Capability.OPENTELEMETRY_TRACER) || + (metricsCapability.isPresent() + && metricsCapability.get().metricsSupported(MetricsFactory.MICROMETER))); + Map>> clientImplementations = new HashMap<>(); Map failures = new HashMap<>(); for (Map.Entry i : result.getClientInterfaces().entrySet()) { @@ -228,7 +239,7 @@ void setupClientProxies(JaxrsClientReactiveRecorder recorder, try { RuntimeValue> proxyProvider = generateClientInvoker(recorderContext, clientProxy, enricherBuildItems, generatedClassBuildItemBuildProducer, clazz, index, defaultConsumesType, - result.getHttpAnnotationToMethod()); + result.getHttpAnnotationToMethod(), observabilityIntegrationNeeded); if (proxyProvider != null) { clientImplementations.put(clientProxy.getClassName(), proxyProvider); } @@ -419,7 +430,8 @@ A more full example of generated client (with sub-resource) can is at the bottom private RuntimeValue> generateClientInvoker(RecorderContext recorderContext, RestClientInterface restClientInterface, List enrichers, BuildProducer generatedClasses, ClassInfo interfaceClass, - IndexView index, String defaultMediaType, Map httpAnnotationToMethod) { + IndexView index, String defaultMediaType, Map httpAnnotationToMethod, + boolean observabilityIntegrationNeeded) { String name = restClientInterface.getClassName() + "$$QuarkusRestClientInterface"; MethodDescriptor ctorDesc = MethodDescriptor.ofConstructor(name, WebTarget.class.getName()); @@ -763,12 +775,22 @@ A more full example of generated client (with sub-resource) can is at the bottom } else { // constructor: initializing the immutable part of the method-specific web target - FieldDescriptor webTargetForMethod = FieldDescriptor.of(name, "target" + methodIndex, WebTarget.class); + FieldDescriptor webTargetForMethod = FieldDescriptor.of(name, "target" + methodIndex, WebTargetImpl.class); c.getFieldCreator(webTargetForMethod).setModifiers(Modifier.FINAL); webTargets.add(webTargetForMethod); AssignableResultHandle constructorTarget = createWebTargetForMethod(constructor, baseTarget, method); constructor.writeInstanceField(webTargetForMethod, constructor.getThis(), constructorTarget); + if (observabilityIntegrationNeeded) { + String templatePath = restClientInterface.getPath() + method.getPath(); + constructor.invokeVirtualMethod( + MethodDescriptor.ofMethod(WebTargetImpl.class, "setPreClientSendHandler", void.class, + ClientRestHandler.class), + constructor.readInstanceField(webTargetForMethod, constructor.getThis()), + constructor.newInstance( + MethodDescriptor.ofConstructor(ClientObservabilityHandler.class, String.class), + constructor.load(templatePath))); + } // generate implementation for a method from jaxrs interface: MethodCreator methodCreator = c.getMethodCreator(method.getName(), method.getSimpleReturnType(), diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientObservabilityHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientObservabilityHandler.java new file mode 100644 index 00000000000000..522297601f0bea --- /dev/null +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientObservabilityHandler.java @@ -0,0 +1,22 @@ +package org.jboss.resteasy.reactive.client.handlers; + +import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; + +/** + * This is added by the Reactive Rest Client if observability features are enabled + */ +@SuppressWarnings("unused") +public class ClientObservabilityHandler implements ClientRestHandler { + + private final String templatePath; + + public ClientObservabilityHandler(String templatePath) { + this.templatePath = templatePath; + } + + @Override + public void handle(RestClientRequestContext requestContext) throws Exception { + requestContext.getClientFilterProperties().put("UrlPathTemplate", templatePath); + } +} diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java index 67beeba86fbe47..1859a440fa6340 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java @@ -22,6 +22,8 @@ class HandlerChain { private final ClientRestHandler clientResponseCompleteRestHandler; private final ClientRestHandler clientErrorHandler; + private ClientRestHandler preClientSendHandler = null; + public HandlerChain(boolean followRedirects) { this.clientSendHandler = new ClientSendRequestHandler(followRedirects); this.clientSetResponseEntityRestHandler = new ClientSetResponseEntityRestHandler(); @@ -29,6 +31,11 @@ public HandlerChain(boolean followRedirects) { this.clientErrorHandler = new ClientErrorHandler(); } + HandlerChain setPreClientSendHandler(ClientRestHandler preClientSendHandler) { + this.preClientSendHandler = preClientSendHandler; + return this; + } + ClientRestHandler[] createHandlerChain(ConfigurationImpl configuration) { List requestFilters = configuration.getRequestFilters(); List responseFilters = configuration.getResponseFilters(); @@ -36,10 +43,14 @@ ClientRestHandler[] createHandlerChain(ConfigurationImpl configuration) { return new ClientRestHandler[] { clientSendHandler, clientSetResponseEntityRestHandler, clientResponseCompleteRestHandler }; } - List result = new ArrayList<>(3 + requestFilters.size() + responseFilters.size()); + List result = new ArrayList<>( + (preClientSendHandler != null ? 4 : 3) + requestFilters.size() + responseFilters.size()); for (int i = 0; i < requestFilters.size(); i++) { result.add(new ClientRequestFilterRestHandler(requestFilters.get(i))); } + if (preClientSendHandler != null) { + result.add(preClientSendHandler); + } result.add(clientSendHandler); result.add(clientSetResponseEntityRestHandler); for (int i = 0; i < responseFilters.size(); i++) { diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java index 35bbc67e822afc..94385b80de4832 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java @@ -361,4 +361,8 @@ public RestClientRequestContext setAbortedWith(Response abortedWith) { public boolean isMultipart() { return entity != null && entity.getEntity() instanceof MultipartForm; } + + public Map getClientFilterProperties() { + return properties; + } } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java index 117196067a3892..3c25ddd03b2cbe 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java @@ -10,6 +10,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriBuilder; +import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.common.jaxrs.UriBuilderImpl; @@ -25,6 +26,10 @@ public class WebTargetImpl implements WebTarget { final HandlerChain handlerChain; final ThreadSetupAction requestContext; + // an additional handler that is passed to the handlerChain + // used to support observability features + private ClientRestHandler preClientSendHandler = null; + public WebTargetImpl(ClientImpl restClient, HttpClient client, UriBuilder uriBuilder, ConfigurationImpl configuration, HandlerChain handlerChain, @@ -260,8 +265,11 @@ public WebTargetImpl queryParamNoTemplate(String name, Object... values) throws protected WebTargetImpl newInstance(HttpClient client, UriBuilder uriBuilder, ConfigurationImpl configuration) { - return new WebTargetImpl(restClient, client, uriBuilder, configuration, handlerChain, + WebTargetImpl result = new WebTargetImpl(restClient, client, uriBuilder, configuration, + handlerChain.setPreClientSendHandler(preClientSendHandler), requestContext); + result.setPreClientSendHandler(preClientSendHandler); + return result; } @Override @@ -296,7 +304,8 @@ private void abortIfClosed() { protected InvocationBuilderImpl createQuarkusRestInvocationBuilder(HttpClient client, UriBuilder uri, ConfigurationImpl configuration) { - return new InvocationBuilderImpl(uri.build(), restClient, client, this, configuration, handlerChain, requestContext); + return new InvocationBuilderImpl(uri.build(), restClient, client, this, configuration, + handlerChain.setPreClientSendHandler(preClientSendHandler), requestContext); } @Override @@ -377,6 +386,11 @@ public ClientImpl getRestClient() { return restClient; } + @SuppressWarnings("unused") + public void setPreClientSendHandler(ClientRestHandler preClientSendHandler) { + this.preClientSendHandler = preClientSendHandler; + } + Serialisers getSerialisers() { return restClient.getClientContext().getSerialisers(); }