From ce53ae097b1b779098ef3d2da42083a29721b93d Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 18 Jan 2023 15:05:05 +0100 Subject: [PATCH] Always use ClientJacksonMessageBodyReader over ServerJacksonMessageBody After https://github.com/quarkusio/quarkus/pull/27203, we can customize the object mappers to be used by REST Client Reactive. However, because of https://github.com/quarkusio/quarkus/pull/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 https://github.com/quarkusio/quarkus/pull/16368. Fix https://github.com/quarkusio/quarkus/issues/23979 --- ...activeJacksonProviderDefinedBuildItem.java | 11 -- .../ResteasyReactiveJacksonProcessor.java | 5 - .../RestClientReactiveJacksonProcessor.java | 12 +- .../ClientWithCustomObjectMapperTest.java | 120 ++++++++++++++++++ .../ClientJacksonMessageBodyReader.java | 3 +- 5 files changed, 128 insertions(+), 23 deletions(-) delete mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java create mode 100644 extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/ClientWithCustomObjectMapperTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java deleted file mode 100644 index bab510942c126..0000000000000 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson-common/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProviderDefinedBuildItem.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.quarkus.resteasy.reactive.jackson.deployment.processor; - -import io.quarkus.builder.item.MultiBuildItem; - -/** - * A BuildItem to mark that the server side jackson provider is defined. - * If not "emitted" by any of the processors, the reactive rest client (if used) will add its own jackson provider - */ -public final class ResteasyReactiveJacksonProviderDefinedBuildItem extends MultiBuildItem { - -} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java index c133d6a33f444..7f0cdfce4cfd8 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java @@ -94,11 +94,6 @@ ServerDefaultProducesHandlerBuildItem jsonDefault() { return ServerDefaultProducesHandlerBuildItem.json(); } - @BuildStep - ResteasyReactiveJacksonProviderDefinedBuildItem jacksonRegistered() { - return new ResteasyReactiveJacksonProviderDefinedBuildItem(); - } - @BuildStep ExceptionMapperBuildItem exceptionMappers() { return new ExceptionMapperBuildItem(DefaultMismatchedInputException.class.getName(), diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java index e057f19afa33e..e50517a6e4195 100644 --- a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/main/java/io/quarkus/rest/client/reactive/jackson/deployment/RestClientReactiveJacksonProcessor.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.List; +import javax.ws.rs.RuntimeType; import javax.ws.rs.core.MediaType; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -15,7 +16,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; @@ -38,13 +38,9 @@ void feature(BuildProducer features) { @BuildStep void additionalProviders( - List jacksonProviderDefined, BuildProducer additionalBean, BuildProducer additionalReaders, BuildProducer 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()) @@ -57,12 +53,14 @@ void additionalProviders( Object.class.getName()) .setMediaTypeStrings(HANDLED_READ_MEDIA_TYPES) .setBuiltin(true) + .setRuntimeType(RuntimeType.CLIENT) .build()); additionalReaders .produce( new MessageBodyReaderBuildItem.Builder(VertxJsonArrayBasicMessageBodyReader.class.getName(), JsonArray.class.getName()) .setMediaTypeStrings(HANDLED_READ_MEDIA_TYPES) + .setRuntimeType(RuntimeType.CLIENT) .setBuiltin(true) .build()); additionalReaders @@ -70,6 +68,7 @@ void additionalProviders( new MessageBodyReaderBuildItem.Builder(VertxJsonObjectBasicMessageBodyReader.class.getName(), JsonObject.class.getName()) .setMediaTypeStrings(HANDLED_READ_MEDIA_TYPES) + .setRuntimeType(RuntimeType.CLIENT) .setBuiltin(true) .build()); additionalWriters @@ -77,6 +76,7 @@ void additionalProviders( new MessageBodyWriterBuildItem.Builder(ClientJacksonMessageBodyWriter.class.getName(), Object.class.getName()) .setMediaTypeStrings(HANDLED_WRITE_MEDIA_TYPES) + .setRuntimeType(RuntimeType.CLIENT) .setBuiltin(true) .build()); additionalWriters @@ -84,6 +84,7 @@ void additionalProviders( new MessageBodyWriterBuildItem.Builder(VertxJsonArrayBasicMessageBodyWriter.class.getName(), JsonArray.class.getName()) .setMediaTypeStrings(HANDLED_WRITE_MEDIA_TYPES) + .setRuntimeType(RuntimeType.CLIENT) .setBuiltin(true) .build()); additionalWriters @@ -91,6 +92,7 @@ void additionalProviders( new MessageBodyWriterBuildItem.Builder(VertxJsonObjectBasicMessageBodyWriter.class.getName(), JsonObject.class.getName()) .setMediaTypeStrings(HANDLED_WRITE_MEDIA_TYPES) + .setRuntimeType(RuntimeType.CLIENT) .setBuiltin(true) .build()); } diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/ClientWithCustomObjectMapperTest.java b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/ClientWithCustomObjectMapperTest.java new file mode 100644 index 0000000000000..9fd849f9d53f2 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/deployment/src/test/java/io/quarkus/rest/client/reactive/jackson/test/ClientWithCustomObjectMapperTest.java @@ -0,0 +1,120 @@ +package io.quarkus.rest.client.reactive.jackson.test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URL; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +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.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 io.quarkus.rest.client.reactive.runtime.RestClientBuilderImpl; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.mutiny.Uni; + +public class ClientWithCustomObjectMapperTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar.addClasses(Resource.class)); + + @TestHTTPResource + URL url; + + MyClient clientAllowsUnknown; + MyClient clientDisallowsUnknown; + + @BeforeEach + public void setUp() { + clientAllowsUnknown = new RestClientBuilderImpl() + .baseUrl(url) + .register(ClientObjectMapperUnknown.class) + .build(MyClient.class); + + clientDisallowsUnknown = new RestClientBuilderImpl() + .baseUrl(url) + .register(ClientObjectMapperNoUnknown.class) + .build(MyClient.class); + } + + @Test + void testCustomObjectMappersShouldBeUsed() { + Request request = new Request(); + request.value = "someValue"; + + // FAIL_ON_UNKNOWN_PROPERTIES disabled + assertThat(clientAllowsUnknown.post(request).await().indefinitely()) + .isInstanceOf(Response.class) + .satisfies(r -> assertThat(r.value).isEqualTo(request.value)); + + // FAIL_ON_UNKNOWN_PROPERTIES enabled + assertThatThrownBy(() -> clientDisallowsUnknown.post(request).await().indefinitely()) + .isInstanceOf(ClientWebApplicationException.class); + } + + @Path("/post") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public interface MyClient { + @POST + Uni post(Request request); + } + + @Path("/post") + public static class Resource { + + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @POST + public ResponseWithSecondValue post(Request request) { + ResponseWithSecondValue response = new ResponseWithSecondValue(); + response.value = request.value; + response.secondValue = "extraValue"; + return response; + } + } + + public static class Request { + public String value; + } + + public static class Response { + public String value; + } + + public static class ResponseWithSecondValue extends Response { + public String secondValue; + } + + public static class ClientObjectMapperUnknown implements ContextResolver { + @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 { + @Override + public ObjectMapper getContext(Class type) { + return new ObjectMapper() + .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .enable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + } + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/ClientJacksonMessageBodyReader.java b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/ClientJacksonMessageBodyReader.java index 662a9a77ee378..9e13462d4d04b 100644 --- a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/ClientJacksonMessageBodyReader.java +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/runtime/serialisers/ClientJacksonMessageBodyReader.java @@ -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; @@ -40,7 +39,7 @@ public Object readFrom(Class 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); } }