Skip to content

Commit

Permalink
Add a cleanup SPI for the REST Client
Browse files Browse the repository at this point in the history
Currently, this is used to cleanup up the mappings
that is needed to support ClientObjectMapper.

The SPI is invoked when the `close` method of a REST
Client is called.

Resolves: #44180
  • Loading branch information
geoand committed Oct 31, 2024
1 parent 45dbff7 commit 46112ec
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -116,4 +119,10 @@ void additionalProviders(
.setRuntimeType(RuntimeType.CLIENT)
.build());
}

@BuildStep
void nativeSupport(BuildProducer<ServiceProviderBuildItem> serviceProviderProducer) {
serviceProviderProducer.produce(new ServiceProviderBuildItem(RestClientClosingTask.class.getName(),
JacksonCleanupRestClientClosingTask.class.getName()));
}
}
Original file line number Diff line number Diff line change
@@ -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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

final class JacksonUtil {

private static final ConcurrentMap<ResolverMapKey, ObjectMapper> contextResolverMap = new ConcurrentHashMap<>();
static final ConcurrentMap<ResolverMapKey, ObjectMapper> contextResolverMap = new ConcurrentHashMap<>();

private JacksonUtil() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.quarkus.rest.client.reactive.jackson.runtime.serialisers.JacksonCleanupRestClientClosingTask
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}

0 comments on commit 46112ec

Please sign in to comment.