diff --git a/extensions/keycloak-admin-client-reactive/runtime/src/main/java/io/quarkus/keycloak/admin/client/reactive/runtime/ResteasyReactiveClientProvider.java b/extensions/keycloak-admin-client-reactive/runtime/src/main/java/io/quarkus/keycloak/admin/client/reactive/runtime/ResteasyReactiveClientProvider.java index 5d44d077b7d74..825e1d6b8454b 100644 --- a/extensions/keycloak-admin-client-reactive/runtime/src/main/java/io/quarkus/keycloak/admin/client/reactive/runtime/ResteasyReactiveClientProvider.java +++ b/extensions/keycloak-admin-client-reactive/runtime/src/main/java/io/quarkus/keycloak/admin/client/reactive/runtime/ResteasyReactiveClientProvider.java @@ -1,9 +1,11 @@ package io.quarkus.keycloak.admin.client.reactive.runtime; +import javax.enterprise.inject.Instance; import javax.net.ssl.SSLContext; import javax.ws.rs.client.Client; import javax.ws.rs.client.WebTarget; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.resteasy.reactive.client.impl.ClientBuilderImpl; import org.jboss.resteasy.reactive.client.impl.WebTargetImpl; import org.jboss.resteasy.reactive.server.jackson.JacksonBasicMessageBodyReader; @@ -14,6 +16,7 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; +import io.quarkus.jackson.ObjectMapperCustomizer; import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyWriter; public class ResteasyReactiveClientProvider implements ResteasyClientProvider { @@ -31,30 +34,56 @@ private ClientBuilderImpl registerJacksonProviders(ClientBuilderImpl clientBuild if (arcContainer == null) { throw new IllegalStateException(this.getClass().getName() + " should only be used in a Quarkus application"); } else { - InstanceHandle objectMapperInstance = arcContainer.instance(ObjectMapper.class); - ObjectMapper objectMapper = null; + boolean canReuseObjectMapper = canReuseObjectMapper(arcContainer); + if (canReuseObjectMapper) { + InstanceHandle objectMapperInstance = arcContainer.instance(ObjectMapper.class); + ObjectMapper objectMapper = null; - InstanceHandle readerInstance = arcContainer - .instance(JacksonBasicMessageBodyReader.class); - if (readerInstance.isAvailable()) { - clientBuilder = clientBuilder.register(readerInstance.get()); - } else { - objectMapper = getObjectMapper(objectMapper, objectMapperInstance); - clientBuilder = clientBuilder.register(new JacksonBasicMessageBodyReader(objectMapper)); - } + InstanceHandle readerInstance = arcContainer + .instance(JacksonBasicMessageBodyReader.class); + if (readerInstance.isAvailable()) { + clientBuilder = clientBuilder.register(readerInstance.get()); + } else { + objectMapper = getObjectMapper(objectMapper, objectMapperInstance); + clientBuilder = clientBuilder.register(new JacksonBasicMessageBodyReader(objectMapper)); + } - InstanceHandle writerInstance = arcContainer - .instance(ClientJacksonMessageBodyWriter.class); - if (writerInstance.isAvailable()) { - clientBuilder = clientBuilder.register(writerInstance.get()); + InstanceHandle writerInstance = arcContainer + .instance(ClientJacksonMessageBodyWriter.class); + if (writerInstance.isAvailable()) { + clientBuilder = clientBuilder.register(writerInstance.get()); + } else { + objectMapper = getObjectMapper(objectMapper, objectMapperInstance); + clientBuilder = clientBuilder.register(new ClientJacksonMessageBodyWriter(objectMapper)); + } } else { - objectMapper = getObjectMapper(objectMapper, objectMapperInstance); - clientBuilder = clientBuilder.register(new ClientJacksonMessageBodyWriter(objectMapper)); + ObjectMapper newObjectMapper = new ObjectMapper(); + clientBuilder = clientBuilder.register(new JacksonBasicMessageBodyReader(newObjectMapper)) + .register(new ClientJacksonMessageBodyWriter(newObjectMapper)); } + } return clientBuilder; } + // the idea is to only reuse the ObjectMapper if no known customizations would break Keycloak + // TODO: in the future we could also look into checking the ObjectMapper bean itself to see how it has been configured + private boolean canReuseObjectMapper(ArcContainer arcContainer) { + Instance customizers = arcContainer.beanManager().createInstance() + .select(ObjectMapperCustomizer.class); + if (!customizers.isUnsatisfied()) { + // ObjectMapperCustomizer can make arbitrary changes, so in order to be safe we won't allow reuse + return false; + } + // if any Jackson properties were configured, disallow reuse - this is done in order to provide forward compatibility with new Jackson configuration options + for (String propertyName : ConfigProvider.getConfig().getPropertyNames()) { + if (propertyName.startsWith("io.quarkus.jackson")) { + return false; + } + } + return true; + } + // the whole idea here is to reuse the ObjectMapper instance private ObjectMapper getObjectMapper(ObjectMapper value, InstanceHandle objectMapperInstance) {