Skip to content

Commit

Permalink
limit scope of alternate ObjectManagers to resteasy reactive extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
chonton committed Mar 2, 2022
1 parent 1d4d592 commit 65a4a35
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 61 deletions.
9 changes: 6 additions & 3 deletions docs/src/main/asciidoc/rest-json.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,11 @@ public class CustomObjectMapper {
}
----

There are three `ObjectMapper` instances provided by the quarkus-jackson extension: unqualified, client, and server. The client
instance is used for REST client serialization/deserialization. The server instance is used for REST server serialization/deserialization.
The unqualified instance is used for general purpose Object to json serialization/deserialization and Object to Object mapping.
There is one unqualified `ObjectMapper` instances provided by the quarkus-jackson extension. You may produce two additional
`ObjectMapper` instances qualified by the `ClientObjectMapper` and `ServerObjectMapper` qualifier annotations. If provided, the
client instance is used by rest-client-reactive extension for serialization/deserialization. If provided, the server
instance is used by quarkus-resteasy-reactive extension for serialization/deserialization. If not a qualified instance is not
provided, the fallback is the unqualified instance.

You can produce, inject, and customize each of these instances independently. The following snippet demonstrates how to inject
each instance using the `ClientObjectMapper` and `ServerObjectMapper` qualifier annotations.
Expand Down Expand Up @@ -345,6 +347,7 @@ import java.util.stream.Stream;
@ApplicationScoped
public class DistinctObjectMapperProducer {
// This producer overrides the default producer provided by resteasy-jackson extension
@Singleton
@Produces
public ObjectMapper unqualifiedObjectMapper(Instance<ObjectMapperCustomizer> unqualifiedCustomizers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.jackson.deployment;

import javax.enterprise.inject.Instance;
import javax.inject.Inject;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.quarkus.jackson.runtime.ClientObjectMapper;
import io.quarkus.jackson.runtime.ServerObjectMapper;
import io.quarkus.test.QuarkusUnitTest;

public class DefaultObjectMappersTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest();

@Inject
@ClientObjectMapper
Instance<ObjectMapper> clientObjectMapper;

@Inject
@ServerObjectMapper
Instance<ObjectMapper> serverObjectMapper;

@Test
public void noClientObjectMapper() {
Assertions.assertTrue(clientObjectMapper.isUnsatisfied());
}

@Test
public void noServerObjectMapper() {
Assertions.assertTrue(serverObjectMapper.isUnsatisfied());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Singleton;

Expand Down Expand Up @@ -81,4 +84,32 @@ public void customize(ObjectMapper objectMapper) {
objectMapper.registerModule(new SimpleModule("server"));
}
}

@Singleton
public static class Factory {

private ObjectMapper createMapper(Instance<ObjectMapperCustomizer> unqualifiedCustomizers,
Instance<ObjectMapperCustomizer> qualifiedCustomizers) {
ObjectMapper objectMapper = new ObjectMapper();
// apply customizers in priority order
Stream.concat(unqualifiedCustomizers.stream(), qualifiedCustomizers.stream())
.sorted()
.forEach(c -> c.customize(objectMapper));
return objectMapper;
}

@Produces
@ClientObjectMapper
ObjectMapper clientObjectMapper(Instance<ObjectMapperCustomizer> unqualifiedCustomizers,
@ClientObjectMapper Instance<ObjectMapperCustomizer> clientCustomizers) {
return createMapper(unqualifiedCustomizers, clientCustomizers);
}

@Produces
@ServerObjectMapper
ObjectMapper serverObjectMapper(Instance<ObjectMapperCustomizer> unqualifiedCustomizers,
@ServerObjectMapper Instance<ObjectMapperCustomizer> serverCustomizers) {
return createMapper(unqualifiedCustomizers, serverCustomizers);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package io.quarkus.jackson.runtime;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import java.util.stream.Stream;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
Expand All @@ -24,34 +26,8 @@ public class ObjectMapperProducer {
@DefaultBean
@Singleton
@Produces
public ObjectMapper unqualifiedObjectMapper(JacksonBuildTimeConfig jacksonBuildTimeConfig,
Instance<ObjectMapperCustomizer> unqualifiedCustomizers) {
return objectMapper(jacksonBuildTimeConfig, unqualifiedCustomizers, null);
}

@DefaultBean
@ClientObjectMapper
@Singleton
@Produces
public ObjectMapper clientObjectMapper(JacksonBuildTimeConfig jacksonBuildTimeConfig,
Instance<ObjectMapperCustomizer> unqualifiedCustomizers,
@ClientObjectMapper Instance<ObjectMapperCustomizer> clientCustomizers) {
return objectMapper(jacksonBuildTimeConfig, unqualifiedCustomizers, clientCustomizers);
}

@DefaultBean
@ServerObjectMapper
@Singleton
@Produces
public ObjectMapper serverObjectMapper(JacksonBuildTimeConfig jacksonBuildTimeConfig,
Instance<ObjectMapperCustomizer> unqualifiedCustomizers,
@ServerObjectMapper Instance<ObjectMapperCustomizer> serverSustomizers) {
return objectMapper(jacksonBuildTimeConfig, unqualifiedCustomizers, serverSustomizers);
}

private ObjectMapper objectMapper(JacksonBuildTimeConfig jacksonBuildTimeConfig,
Instance<ObjectMapperCustomizer> unqualifiedCustomizers, Instance<ObjectMapperCustomizer> qualifiedCustomizers) {

public ObjectMapper objectMapper(Instance<ObjectMapperCustomizer> customizers,
JacksonBuildTimeConfig jacksonBuildTimeConfig) {
ObjectMapper objectMapper = new ObjectMapper();
if (!jacksonBuildTimeConfig.failOnUnknownProperties) {
// this feature is enabled by default, so we disable it
Expand All @@ -76,16 +52,20 @@ private ObjectMapper objectMapper(JacksonBuildTimeConfig jacksonBuildTimeConfig,
if ((zoneId != null) && !zoneId.getId().equals("UTC")) { // Jackson uses UTC as the default, so let's not reset it
objectMapper.setTimeZone(TimeZone.getTimeZone(zoneId));
}

// concat all customizers
Stream<ObjectMapperCustomizer> customizers = unqualifiedCustomizers.stream();
if (qualifiedCustomizers != null) {
customizers = Stream.concat(customizers, qualifiedCustomizers.stream());
List<ObjectMapperCustomizer> sortedCustomizers = sortCustomizersInDescendingPriorityOrder(customizers);
for (ObjectMapperCustomizer customizer : sortedCustomizers) {
customizer.customize(objectMapper);
}

// apply customizers in priority order
customizers.sorted().forEach(c -> c.customize(objectMapper));
return objectMapper;
}

private List<ObjectMapperCustomizer> sortCustomizersInDescendingPriorityOrder(
Instance<ObjectMapperCustomizer> customizers) {
List<ObjectMapperCustomizer> sortedCustomizers = new ArrayList<>();
for (ObjectMapperCustomizer customizer : customizers) {
sortedCustomizers.add(customizer);
}
Collections.sort(sortedCustomizers);
return sortedCustomizers;
}
}
5 changes: 0 additions & 5 deletions extensions/resteasy-classic/resteasy-common/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,6 @@
<artifactId>resteasy-json-binding-provider</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@

import com.fasterxml.jackson.databind.ObjectMapper;

import io.quarkus.jackson.runtime.ServerObjectMapper;

@Provider
@ApplicationScoped
@Priority(Priorities.USER + 10) // give it a priority that ensures that user supplied ContextResolver classes override this one
public class QuarkusObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

@ServerObjectMapper
@Inject
ObjectMapper objectMapper;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.function.BiFunction;
import java.util.function.Function;

import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
Expand All @@ -37,9 +38,10 @@ public class FullyFeaturedServerJacksonMessageBodyWriter extends ServerMessageBo
private final ConcurrentMap<String, ObjectWriter> perMethodWriter = new ConcurrentHashMap<>();

@Inject
public FullyFeaturedServerJacksonMessageBodyWriter(@ServerObjectMapper ObjectMapper mapper) {
this.originalMapper = mapper;
this.defaultWriter = createDefaultWriter(mapper);
public FullyFeaturedServerJacksonMessageBodyWriter(@ServerObjectMapper Instance<ObjectMapper> serverInstance,
ObjectMapper mapper) {
this.originalMapper = serverInstance.isUnsatisfied() ? mapper : serverInstance.get();
this.defaultWriter = createDefaultWriter(originalMapper);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
Expand All @@ -25,9 +26,9 @@ public class ClientJacksonMessageBodyWriter implements MessageBodyWriter<Object>
protected final ObjectWriter defaultWriter;

@Inject
public ClientJacksonMessageBodyWriter(@ClientObjectMapper ObjectMapper mapper) {
this.originalMapper = mapper;
this.defaultWriter = createDefaultWriter(mapper);
public ClientJacksonMessageBodyWriter(@ClientObjectMapper Instance<ObjectMapper> clientInstance, ObjectMapper mapper) {
this.originalMapper = clientInstance.isUnsatisfied() ? mapper : clientInstance.get();
this.defaultWriter = createDefaultWriter(originalMapper);
}

@Override
Expand Down
5 changes: 0 additions & 5 deletions extensions/vertx/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import io.netty.buffer.ByteBufInputStream;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.jackson.runtime.ServerObjectMapper;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.EncodeException;
Expand All @@ -41,7 +40,7 @@ class QuarkusJacksonJsonCodec implements JsonCodec {
// this can happen in QuarkusUnitTest
mapper = new ObjectMapper();
} else {
ObjectMapper managedMapper = container.instance(ObjectMapper.class, ServerObjectMapper.Literal.INSTANCE).get();
ObjectMapper managedMapper = container.instance(ObjectMapper.class).get();
if (managedMapper == null) {
// TODO: is this too heavy handed? It should never happen but even if it does, it's a mostly recoverable state
throw new IllegalStateException("There was no ObjectMapper bean configured");
Expand Down

0 comments on commit 65a4a35

Please sign in to comment.