diff --git a/extensions/resteasy-reactive/rest-client-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java b/extensions/resteasy-reactive/rest-client-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java index 51bf0bc4fbf41..0570168c2f50d 100644 --- a/extensions/resteasy-reactive/rest-client-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/rest-client-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java @@ -10,6 +10,7 @@ import jakarta.ws.rs.core.MediaType; import org.jboss.jandex.DotName; +import org.jboss.resteasy.reactive.client.impl.RestClientClosingTask; import com.fasterxml.jackson.databind.ObjectMapper; @@ -18,10 +19,12 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.rest.client.reactive.deployment.AnnotationToRegisterIntoClientContextBuildItem; import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper; import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyReader; import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyWriter; +import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.JacksonCleanupRestClientClosingTask; import io.quarkus.resteasy.reactive.jackson.deployment.processor.ResteasyReactiveJacksonProviderDefinedBuildItem; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayBasicMessageBodyReader; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayBasicMessageBodyWriter; @@ -116,4 +119,10 @@ void additionalProviders( .setRuntimeType(RuntimeType.CLIENT) .build()); } + + @BuildStep + void nativeSupport(BuildProducer serviceProviderProducer) { + serviceProviderProducer.produce(new ServiceProviderBuildItem(RestClientClosingTask.class.getName(), + JacksonCleanupRestClientClosingTask.class.getName())); + } } diff --git a/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonCleanupRestClientClosingTask.java b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonCleanupRestClientClosingTask.java new file mode 100644 index 0000000000000..861e2888820af --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonCleanupRestClientClosingTask.java @@ -0,0 +1,17 @@ +package io.quarkus.rest.client.reactive.jackson.runtime.serialisers; + +import org.jboss.resteasy.reactive.client.impl.RestClientClosingTask; + +import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper; + +/** + * Cleans up the mappings that is needed to support {@link ClientObjectMapper} + */ +public class JacksonCleanupRestClientClosingTask implements RestClientClosingTask { + + @Override + public void close(Context context) { + JacksonUtil.contextResolverMap + .remove(new ResolverMapKey(context.baseTarget().getConfiguration(), context.restApiClass())); + } +} diff --git a/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonUtil.java b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonUtil.java index e392db2821c6d..8f3bf71ef343b 100644 --- a/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonUtil.java +++ b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/JacksonUtil.java @@ -14,7 +14,7 @@ final class JacksonUtil { - private static final ConcurrentMap contextResolverMap = new ConcurrentHashMap<>(); + static final ConcurrentMap contextResolverMap = new ConcurrentHashMap<>(); private JacksonUtil() { } diff --git a/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.reactive.client.impl.RestClientClosingTask b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.reactive.client.impl.RestClientClosingTask new file mode 100644 index 0000000000000..cdd0672e468f5 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.reactive.client.impl.RestClientClosingTask @@ -0,0 +1 @@ +io.quarkus.rest.client.reactive.jackson.runtime.serialisers.JacksonCleanupRestClientClosingTask diff --git a/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index d8b3eb056ffe1..3733ef6ec9ef0 100644 --- a/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -89,6 +89,7 @@ import org.jboss.resteasy.reactive.client.impl.ClientBuilderImpl; import org.jboss.resteasy.reactive.client.impl.ClientImpl; import org.jboss.resteasy.reactive.client.impl.MultiInvoker; +import org.jboss.resteasy.reactive.client.impl.RestClientClosingTask; import org.jboss.resteasy.reactive.client.impl.SseEventSourceBuilderImpl; import org.jboss.resteasy.reactive.client.impl.StorkClientRequestFilter; import org.jboss.resteasy.reactive.client.impl.UniInvoker; @@ -1214,6 +1215,15 @@ A more full example of generated client (with sub-resource) can is at the bottom .getMethodCreator(MethodDescriptor.ofMethod(Closeable.class, "close", void.class)); ResultHandle webTarget = closeCreator.readInstanceField(baseTargetField, closeCreator.getThis()); ResultHandle webTargetImpl = closeCreator.checkCast(webTarget, WebTargetImpl.class); + ResultHandle restApiClass = closeCreator.loadClassFromTCCL(restClientInterface.getClassName()); + ResultHandle context = closeCreator.newInstance( + MethodDescriptor.ofConstructor(RestClientClosingTask.Context.class, Class.class, WebTargetImpl.class), + restApiClass, + webTargetImpl); + closeCreator.invokeStaticInterfaceMethod( + MethodDescriptor.ofMethod(RestClientClosingTask.class, "invokeAll", void.class, + RestClientClosingTask.Context.class), + context); ResultHandle restClient = closeCreator.invokeVirtualMethod( MethodDescriptor.ofMethod(WebTargetImpl.class, "getRestClient", ClientImpl.class), webTargetImpl); closeCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(ClientImpl.class, "close", void.class), restClient); diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientClosingTask.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientClosingTask.java new file mode 100644 index 0000000000000..0e7834d6076ab --- /dev/null +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientClosingTask.java @@ -0,0 +1,30 @@ +package org.jboss.resteasy.reactive.client.impl; + +import java.util.ServiceLoader; + +import org.jboss.logging.Logger; + +/** + * This represents a task that will be called by the implementation class of the REST Client when the {@code close} + * method is called. + */ +public interface RestClientClosingTask { + + Logger log = Logger.getLogger(RestClientClosingTask.class); + + void close(Context context); + + record Context(Class restApiClass, WebTargetImpl baseTarget) { + } + + @SuppressWarnings("unused") // this is called by the implementation class of the REST Client when the {@code close} method is called + static void invokeAll(Context context) { + for (RestClientClosingTask restClientClosingTask : ServiceLoader.load(RestClientClosingTask.class)) { + try { + restClientClosingTask.close(context); + } catch (Exception e) { + log.warn("Error running RestClientClosingTask", e); + } + } + } +}