Skip to content

Commit

Permalink
The JacksonCodec implementation of toString/toBuffer lacks of efficie…
Browse files Browse the repository at this point in the history
…ncy compared to the DatabindCodec implementation which uses the buffer recyler of Jackson.

Update the JacksonCodec implementation to use the buffer recycler instead.
  • Loading branch information
vietj committed Jul 7, 2024
1 parent 5d0d5d9 commit fa52bf4
Showing 1 changed file with 42 additions and 33 deletions.
75 changes: 42 additions & 33 deletions src/main/java/io/vertx/core/json/jackson/JacksonCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
package io.vertx.core.json.jackson;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.SegmentedStringWriter;
import com.fasterxml.jackson.core.type.TypeReference;
import io.netty.buffer.ByteBuf;
import com.fasterxml.jackson.core.util.BufferRecycler;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.EncodeException;
Expand All @@ -28,7 +28,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
Expand All @@ -50,33 +49,37 @@
*/
public class JacksonCodec implements JsonCodec {

private static JsonFactory buildFactory() {
private static final class JacksonPoolHolder {
// Use Initialization-on-demand holder idiom to lazy load the HybridJacksonPool only when we know that we are on Jackson 2.16+
private static final Object pool = HybridJacksonPool.getInstance();
}

static final boolean releaseToPool;
static final JsonFactory factory;

static {

boolean releaseToPoolValue = false;
TSFBuilder<?, ?> builder = JsonFactory.builder();
try {
// Use reflection to configure the recycler pool
Method[] methods = builder.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals("recyclerPool")) {
method.invoke(builder, JacksonPoolHolder.pool);
releaseToPoolValue = true;
break;
}
}
} catch (Throwable e) {
// Ignore: most likely no Recycler Pool with Jackson < 2.16
}
return builder.build();
}

private static final class JacksonPoolHolder {
// Use Initialization-on-demand holder idiom to lazy load the HybridJacksonPool only when we know that we are on Jackson 2.16+
private static final Object pool = HybridJacksonPool.getInstance();
}

static final JsonFactory factory = buildFactory();
factory = builder.build();
releaseToPool = releaseToPoolValue;

static {
// Non-standard JSON but we allow C style comments in our JSON
JacksonCodec.factory.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
factory.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
}

@Override
Expand Down Expand Up @@ -108,38 +111,44 @@ public <T> T fromValue(Object json, TypeReference<T> type) {

@Override
public String toString(Object object, boolean pretty) throws EncodeException {
StringWriter sw = new StringWriter();
JsonGenerator generator = createGenerator(sw, pretty);
try {
BufferRecycler br = factory._getBufferRecycler();
try (SegmentedStringWriter sw = new SegmentedStringWriter(br)) {
JsonGenerator generator = createGenerator(sw, pretty);
encodeJson(object, generator);
generator.flush();
return sw.toString();
generator.close();
return sw.getAndClear();
} catch (IOException e) {
throw new EncodeException(e.getMessage(), e);
} finally {
close(generator);
if (releaseToPool) {
releaseToPool(br);
}
}
}

@Override
public Buffer toBuffer(Object object, boolean pretty) throws EncodeException {
ByteBuf buf = Unpooled.buffer();
// There is no need to use a try with resources here as jackson
// is a well-behaved and always calls the closes all streams in the
// "finally" block bellow.
ByteBufOutputStream out = new ByteBufOutputStream(buf);
JsonGenerator generator = createGenerator(out, pretty);
try {
BufferRecycler br = factory._getBufferRecycler();
try (ByteArrayBuilder bb = new ByteArrayBuilder(br)) {
JsonGenerator generator = createGenerator(bb, pretty);
encodeJson(object, generator);
generator.flush();
return Buffer.buffer(buf);
} catch (IOException e) {
generator.close();
byte[] result = bb.toByteArray();
bb.release();
return Buffer.buffer(result);
} catch (java.io.IOException e) {
throw new EncodeException(e.getMessage(), e);
} finally {
close(generator);
if (releaseToPool) {
releaseToPool(br);
}
}
}

private static void releaseToPool(BufferRecycler br) {
br.releaseToPool();
}

public static JsonParser createParser(String str) {
try {
return factory.createParser(str);
Expand Down Expand Up @@ -286,7 +295,7 @@ static void close(Closeable parser) {
}

// In recursive calls, the callee is in charge of opening and closing the data structure
private static void encodeJson(Object json, JsonGenerator generator) throws EncodeException {
public static void encodeJson(Object json, JsonGenerator generator) throws EncodeException {
try {
if (json instanceof JsonObject) {
json = ((JsonObject)json).getMap();
Expand Down

0 comments on commit fa52bf4

Please sign in to comment.