Skip to content

Commit

Permalink
Merge pull request #16578 from geoand/#14720-again
Browse files Browse the repository at this point in the history
Ensure that Jackson writer doesn't negatively affect endpoint score
  • Loading branch information
geoand authored Apr 16, 2021
2 parents bfbd433 + bc4fbf6 commit a9a1dc7
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 111 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.quarkus.resteasy.reactive.jackson.runtime.serialisers;

import static org.jboss.resteasy.reactive.common.providers.serialisers.JsonMessageBodyWriterUtil.setContentTypeIfNecessary;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;

import javax.ws.rs.core.MultivaluedMap;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

public final class JacksonMessageBodyWriterUtil {

private JacksonMessageBodyWriterUtil() {
}

public static ObjectWriter createDefaultWriter(ObjectMapper mapper) {
// we don't want the ObjectWriter to close the stream automatically, as we want to handle closing manually at the proper points
JsonFactory jsonFactory = mapper.getFactory();
if (JacksonMessageBodyWriterUtil.needsNewFactory(jsonFactory)) {
jsonFactory = jsonFactory.copy();
JacksonMessageBodyWriterUtil.setNecessaryJsonFactoryConfig(jsonFactory);
return mapper.writer().with(jsonFactory);
} else {
return mapper.writer();
}
}

private static boolean needsNewFactory(JsonFactory jsonFactory) {
return jsonFactory.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)
|| jsonFactory.isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM);
}

public static void setNecessaryJsonFactoryConfig(JsonFactory jsonFactory) {
jsonFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
jsonFactory.configure(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM, false);
}

public static void doLegacyWrite(Object o, Annotation[] annotations, MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream, ObjectWriter defaultWriter) throws IOException {
setContentTypeIfNecessary(httpHeaders);
if (o instanceof String) { // YUK: done in order to avoid adding extra quotes...
entityStream.write(((String) o).getBytes());
} else {
if (annotations != null) {
for (Annotation annotation : annotations) {
if (JsonView.class.equals(annotation.annotationType())) {
if (handleJsonView(((JsonView) annotation), o, entityStream, defaultWriter)) {
return;
}
}
}
}
entityStream.write(defaultWriter.writeValueAsBytes(o));
}
}

private static boolean handleJsonView(JsonView jsonView, Object o, OutputStream stream, ObjectWriter defaultWriter)
throws IOException {
if ((jsonView != null) && (jsonView.value().length > 0)) {
defaultWriter.withView(jsonView.value()[0]).writeValue(stream, o);
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.resteasy.reactive.common.deployment.ServerDefaultProducesHandlerBuildItem;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyReader;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyWriter;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyReader;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyWriter;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayMessageBodyReader;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayMessageBodyWriter;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonObjectMessageBodyReader;
Expand Down Expand Up @@ -44,12 +44,12 @@ void additionalProviders(BuildProducer<AdditionalBeanBuildItem> additionalBean,
BuildProducer<MessageBodyWriterBuildItem> additionalWriters) {
// make these beans to they can get instantiated with the Quarkus CDI configured Jsonb object
additionalBean.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(JacksonMessageBodyReader.class.getName())
.addBeanClass(JacksonMessageBodyWriter.class.getName())
.addBeanClass(ServerJacksonMessageBodyReader.class.getName())
.addBeanClass(ServerJacksonMessageBodyWriter.class.getName())
.setUnremovable().build());

additionalReaders
.produce(new MessageBodyReaderBuildItem(JacksonMessageBodyReader.class.getName(), Object.class.getName(),
.produce(new MessageBodyReaderBuildItem(ServerJacksonMessageBodyReader.class.getName(), Object.class.getName(),
Collections.singletonList(MediaType.APPLICATION_JSON)));
additionalReaders
.produce(new MessageBodyReaderBuildItem(VertxJsonArrayMessageBodyReader.class.getName(),
Expand All @@ -60,7 +60,7 @@ void additionalProviders(BuildProducer<AdditionalBeanBuildItem> additionalBean,
JsonObject.class.getName(),
Collections.singletonList(MediaType.APPLICATION_JSON)));
additionalWriters
.produce(new MessageBodyWriterBuildItem(JacksonMessageBodyWriter.class.getName(), Object.class.getName(),
.produce(new MessageBodyWriterBuildItem(ServerJacksonMessageBodyWriter.class.getName(), Object.class.getName(),
Collections.singletonList(MediaType.APPLICATION_JSON)));
additionalWriters
.produce(new MessageBodyWriterBuildItem(VertxJsonArrayMessageBodyWriter.class.getName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;

public class JacksonMessageBodyReader extends JacksonBasicMessageBodyReader implements ServerMessageBodyReader<Object> {
public class ServerJacksonMessageBodyReader extends JacksonBasicMessageBodyReader implements ServerMessageBodyReader<Object> {

@Inject
public JacksonMessageBodyReader(ObjectMapper mapper) {
public ServerJacksonMessageBodyReader(ObjectMapper mapper) {
super(mapper);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package io.quarkus.resteasy.reactive.jackson.runtime.serialisers;

import static io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyWriterUtil.*;
import static org.jboss.resteasy.reactive.server.vertx.providers.serialisers.json.JsonMessageServerBodyWriterUtil.setContentTypeIfNecessary;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
import java.util.function.Function;

import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;

import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo;
import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter;
Expand All @@ -24,14 +29,19 @@

import io.quarkus.resteasy.reactive.jackson.CustomSerialization;

public class JacksonMessageBodyWriter extends JacksonBasicMessageBodyWriter implements ServerMessageBodyWriter<Object> {// extends ServerMessageBodyWriter.AllWriteableMessageBodyWriter {
public class ServerJacksonMessageBodyWriter extends ServerMessageBodyWriter.AllWriteableMessageBodyWriter {

private static final String JSON_VIEW_NAME = JsonView.class.getName();
private static final String CUSTOM_SERIALIZATION = CustomSerialization.class.getName();

private final ObjectMapper originalMapper;
private final ObjectWriter defaultWriter;
private final ConcurrentMap<Method, ObjectWriter> perMethodWriter = new ConcurrentHashMap<>();

@Inject
public JacksonMessageBodyWriter(ObjectMapper mapper) {
super(mapper);
public ServerJacksonMessageBodyWriter(ObjectMapper mapper) {
this.originalMapper = mapper;
this.defaultWriter = createDefaultWriter(mapper);
}

@Override
Expand Down Expand Up @@ -65,12 +75,6 @@ public void writeResponse(Object o, Type genericType, ServerRequestContext conte
stream.close();
}

@Override
public final boolean isWriteable(Class<?> type, Type genericType, ResteasyReactiveResourceInfo target,
MediaType mediaType) {
return true;
}

// TODO: this can definitely be made faster if necessary by optimizing the use of the map and also by moving the creation of the
// biFunction to build time
private boolean handleCustomSerialization(Method method, Object o, Type genericType, OutputStream stream)
Expand All @@ -94,6 +98,12 @@ private boolean handleJsonView(JsonView jsonView, Object o, OutputStream stream)
return false;
}

@Override
public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
doLegacyWrite(o, annotations, httpHeaders, entityStream, defaultWriter);
}

private static class MethodObjectWriterFunction implements Function<Method, ObjectWriter> {
private final Class<? extends BiFunction<ObjectMapper, Type, ObjectWriter>> clazz;
private final Type genericType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
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.JacksonBasicMessageBodyReader;
import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonBasicMessageBodyWriter;
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 Down Expand Up @@ -42,7 +42,7 @@ void additionalProviders(
// make these beans to they can get instantiated with the Quarkus CDI configured Jsonb object
additionalBean.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(JacksonBasicMessageBodyReader.class.getName())
.addBeanClass(JacksonBasicMessageBodyWriter.class.getName())
.addBeanClass(ClientJacksonMessageBodyWriter.class.getName())
.setUnremovable().build());

additionalReaders
Expand All @@ -57,7 +57,7 @@ void additionalProviders(
JsonObject.class.getName(),
Collections.singletonList(MediaType.APPLICATION_JSON)));
additionalWriters
.produce(new MessageBodyWriterBuildItem(JacksonBasicMessageBodyWriter.class.getName(), Object.class.getName(),
.produce(new MessageBodyWriterBuildItem(ClientJacksonMessageBodyWriter.class.getName(), Object.class.getName(),
Collections.singletonList(MediaType.APPLICATION_JSON)));
additionalWriters
.produce(new MessageBodyWriterBuildItem(VertxJsonArrayBasicMessageBodyWriter.class.getName(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.quarkus.rest.client.reactive.jackson.runtime.serialisers;

import static io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyWriterUtil.createDefaultWriter;
import static io.quarkus.resteasy.reactive.jackson.runtime.serialisers.JacksonMessageBodyWriterUtil.doLegacyWrite;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

public class ClientJacksonMessageBodyWriter implements MessageBodyWriter<Object> {

protected final ObjectMapper originalMapper;
protected final ObjectWriter defaultWriter;

@Inject
public ClientJacksonMessageBodyWriter(ObjectMapper mapper) {
this.originalMapper = mapper;
this.defaultWriter = createDefaultWriter(mapper);
}

@Override
public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}

@Override
public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
doLegacyWrite(o, annotations, httpHeaders, entityStream, defaultWriter);
}
}

0 comments on commit a9a1dc7

Please sign in to comment.