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 d72072c commit d1ecdc2
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

/**
* 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
* @Deprecated because now the rest client reactive will always add its own jackson provider.
*/
@Deprecated(forRemoval = true)
public final class ResteasyReactiveJacksonProviderDefinedBuildItem extends MultiBuildItem {

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -38,13 +38,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 All @@ -57,40 +53,46 @@ 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
.produce(
new MessageBodyReaderBuildItem.Builder(VertxJsonObjectBasicMessageBodyReader.class.getName(),
JsonObject.class.getName())
.setMediaTypeStrings(HANDLED_READ_MEDIA_TYPES)
.setRuntimeType(RuntimeType.CLIENT)
.setBuiltin(true)
.build());
additionalWriters
.produce(
new MessageBodyWriterBuildItem.Builder(ClientJacksonMessageBodyWriter.class.getName(),
Object.class.getName())
.setMediaTypeStrings(HANDLED_WRITE_MEDIA_TYPES)
.setRuntimeType(RuntimeType.CLIENT)
.setBuiltin(true)
.build());
additionalWriters
.produce(
new MessageBodyWriterBuildItem.Builder(VertxJsonArrayBasicMessageBodyWriter.class.getName(),
JsonArray.class.getName())
.setMediaTypeStrings(HANDLED_WRITE_MEDIA_TYPES)
.setRuntimeType(RuntimeType.CLIENT)
.setBuiltin(true)
.build());
additionalWriters
.produce(
new MessageBodyWriterBuildItem.Builder(VertxJsonObjectBasicMessageBodyWriter.class.getName(),
JsonObject.class.getName())
.setMediaTypeStrings(HANDLED_WRITE_MEDIA_TYPES)
.setRuntimeType(RuntimeType.CLIENT)
.setBuiltin(true)
.build());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Response> 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<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 d1ecdc2

Please sign in to comment.