Skip to content

Commit

Permalink
Use custom ObjectMapper for Keycloak admin client if necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Jan 23, 2023
1 parent cd3bb03 commit 29bbb1f
Showing 1 changed file with 45 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand All @@ -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<ObjectMapper> objectMapperInstance = arcContainer.instance(ObjectMapper.class);
ObjectMapper objectMapper = null;
boolean canReuseObjectMapper = canReuseObjectMapper(arcContainer);
if (canReuseObjectMapper) {
InstanceHandle<ObjectMapper> objectMapperInstance = arcContainer.instance(ObjectMapper.class);
ObjectMapper objectMapper = null;

InstanceHandle<JacksonBasicMessageBodyReader> 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<JacksonBasicMessageBodyReader> 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<ClientJacksonMessageBodyWriter> writerInstance = arcContainer
.instance(ClientJacksonMessageBodyWriter.class);
if (writerInstance.isAvailable()) {
clientBuilder = clientBuilder.register(writerInstance.get());
InstanceHandle<ClientJacksonMessageBodyWriter> 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<ObjectMapperCustomizer> 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<ObjectMapper> objectMapperInstance) {
Expand Down

0 comments on commit 29bbb1f

Please sign in to comment.