Skip to content

Commit

Permalink
Always use ClientJacksonMessageBodyReader over ServerJacksonMessageBody
Browse files Browse the repository at this point in the history
After quarkusio#27203, we can customize the object mappers to be used by REST Client Reactive.
However, because of quarkusio#16368, the implementation was never picked up when the resteasy reactive jackson extension is in place. 
I tried to remove the ResteasyReactiveJacksonProviderDefinedBuildItem build item and surprisingly everything kept working fine (I verified the test that was added as part of  quarkusio#16368.

Fix quarkusio#23979
  • Loading branch information
Sgitario committed Jan 18, 2023
1 parent 832c06f commit de42b89
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 23 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ ServerDefaultProducesHandlerBuildItem jsonDefault() {
return ServerDefaultProducesHandlerBuildItem.json();
}

@BuildStep
ResteasyReactiveJacksonProviderDefinedBuildItem jacksonRegistered() {
return new ResteasyReactiveJacksonProviderDefinedBuildItem();
}

@BuildStep
ExceptionMapperBuildItem exceptionMappers() {
return new ExceptionMapperBuildItem(DefaultMismatchedInputException.class.getName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyReader;
import io.quarkus.rest.client.reactive.jackson.runtime.serialisers.ClientJacksonMessageBodyWriter;
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;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonObjectBasicMessageBodyReader;
Expand All @@ -38,13 +37,9 @@ void feature(BuildProducer<FeatureBuildItem> features) {

@BuildStep
void additionalProviders(
List<ResteasyReactiveJacksonProviderDefinedBuildItem> jacksonProviderDefined,
BuildProducer<AdditionalBeanBuildItem> additionalBean,
BuildProducer<MessageBodyReaderBuildItem> additionalReaders,
BuildProducer<MessageBodyWriterBuildItem> additionalWriters) {
if (!jacksonProviderDefined.isEmpty()) {
return;
}
// make these beans to they can get instantiated with the Quarkus CDI configured Jsonb object
additionalBean.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(ClientJacksonMessageBodyReader.class.getName())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package io.quarkus.rest.client.reactive.jackson.test;

import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Objects;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;

import org.jboss.resteasy.reactive.ClientWebApplicationException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;

import io.quarkus.rest.client.reactive.runtime.RestClientBuilderImpl;
import io.quarkus.test.QuarkusUnitTest;
import io.smallrye.mutiny.Uni;

public class ClientWithCustomObjectMapperTest {

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

MyClient clientAllowsUnknown;
MyClient clientDisallowsUnknown;
WireMockServer wireMockServer;

@BeforeEach
public void setUp() throws MalformedURLException {
wireMockServer = new WireMockServer(options().port(20001));
wireMockServer.start();

clientAllowsUnknown = new RestClientBuilderImpl()
.baseUrl(new URL(wireMockServer.baseUrl()))
.register(ClientObjectMapperUnknown.class)
.build(MyClient.class);

clientDisallowsUnknown = new RestClientBuilderImpl()
.baseUrl(new URL(wireMockServer.baseUrl()))
.register(ClientObjectMapperNoUnknown.class)
.build(MyClient.class);
}

@AfterEach
public void tearDown() {
wireMockServer.stop();
}

@Test
void testCustomObjectMappersShouldBeUsed() {
var json = "{ \"value\": \"someValue\", \"secondValue\": \"toBeIgnored\" }";
wireMockServer.stubFor(
WireMock.get(WireMock.urlMatching("/get"))
.willReturn(okJson(json)));

// FAIL_ON_UNKNOWN_PROPERTIES disabled
assertThat(clientAllowsUnknown.get().await().indefinitely())
.isEqualTo(new Request("someValue"));

// FAIL_ON_UNKNOWN_PROPERTIES enabled
assertThatThrownBy(() -> clientDisallowsUnknown.get().await().indefinitely())
.isInstanceOf(ClientWebApplicationException.class);
}

@Path("/get")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface MyClient {
@GET
Uni<Request> get();
}

public static class Request {
private String value;

public Request() {

}

public Request(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Request request = (Request) o;
return Objects.equals(value, request.value);
}

@Override
public int hashCode() {
return Objects.hash(value);
}
}

public static class ClientObjectMapperUnknown implements ContextResolver<ObjectMapper> {
@Override
public ObjectMapper getContext(Class<?> type) {
return new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
}

public static class ClientObjectMapperNoUnknown implements ContextResolver<ObjectMapper> {
@Override
public ObjectMapper getContext(Class<?> type) {
return new ObjectMapper()
.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.reactive.ClientWebApplicationException;
import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;
import org.jboss.resteasy.reactive.client.spi.ClientRestHandler;
import org.jboss.resteasy.reactive.server.jackson.JacksonBasicMessageBodyReader;
Expand All @@ -40,7 +39,7 @@ public Object readFrom(Class<Object> type, Type genericType, Annotation[] annota
try {
return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream);
} catch (StreamReadException | DatabindException e) {
throw new ClientWebApplicationException(e, Response.Status.BAD_REQUEST);
throw new WebApplicationException(e, Response.Status.BAD_REQUEST);
}
}

Expand Down

0 comments on commit de42b89

Please sign in to comment.