From decd9c223e559d9b41f7511f4142c1a2a91dff70 Mon Sep 17 00:00:00 2001 From: Brian Demers Date: Mon, 12 Jul 2021 15:45:30 -0400 Subject: [PATCH] Updates Jackson usage to use immutable ObjectReader/Writer instead of ObjectMapper Jackson 2.10+ recommend using `ObjectReader` and `ObjectWriter` as opposed to `ObjectMapper` https://cowtowncoder.medium.com/jackson-3-0-immutability-w-builders-d9c532860d88 --- CHANGELOG.md | 7 +++++++ .../jackson/io/JacksonDeserializer.java | 21 ++++++++++++------- .../jackson/io/JacksonSerializer.java | 8 ++++--- .../jackson/io/JacksonDeserializerTest.groovy | 6 +++--- .../jackson/io/JacksonSerializerTest.groovy | 9 +++++--- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a655007..eff5c7e3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## Release Notes +### 0.12.8 (pending release) + +This patch release: +* +* Updates Jackson usage (in `jjwt-jackson`) to use immutable classes instead of using `ObjectMapper` directly. + ### 0.12.7 This patch release: @@ -445,6 +451,7 @@ provided the JJWT team. This patch release: * Adds additional handling for rare JSON parsing exceptions and wraps them in a `JwtException` to allow the application to handle these conditions as JWT concerns. +* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.12.4`. * Upgrades the `jjwt-jackson` module's Jackson dependency to `2.12.6.1`. * Upgrades the `jjwt-orgjson` module's org.json:json dependency to `20220320`. * Upgrades the `jjwt-gson` module's gson dependency to `2.9.0`. diff --git a/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonDeserializer.java b/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonDeserializer.java index 63cc37618..eb07555b8 100644 --- a/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonDeserializer.java +++ b/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonDeserializer.java @@ -17,7 +17,9 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import io.jsonwebtoken.io.AbstractDeserializer; @@ -37,7 +39,7 @@ public class JacksonDeserializer extends AbstractDeserializer { private final Class returnType; - private final ObjectMapper objectMapper; + private final ObjectReader objectReader; /** * Constructor using JJWT's default {@link ObjectMapper} singleton for deserialization. @@ -116,24 +118,27 @@ public JacksonDeserializer(ObjectMapper objectMapper) { * @since 0.12.4 */ public JacksonDeserializer(ObjectMapper objectMapper, Map> claimTypeMap) { - this(objectMapper); - Assert.notNull(claimTypeMap, "Claim type map cannot be null."); // register a new Deserializer on the ObjectMapper instance: - SimpleModule module = new SimpleModule(); - module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap))); - objectMapper.registerModule(module); + this(objectMapper.registerModule(mappedTypeModule(claimTypeMap))); } private JacksonDeserializer(ObjectMapper objectMapper, Class returnType) { Assert.notNull(objectMapper, "ObjectMapper cannot be null."); Assert.notNull(returnType, "Return type cannot be null."); - this.objectMapper = objectMapper; + this.objectReader = objectMapper.reader(); this.returnType = returnType; } @Override protected T doDeserialize(Reader reader) throws Exception { - return objectMapper.readValue(reader, returnType); + return objectReader.readValue(reader, returnType); + } + + private static Module mappedTypeModule(Map> claimTypeMap) { + Assert.notNull(claimTypeMap, "Claim type map cannot be null."); + SimpleModule module = new SimpleModule(); + module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap))); + return module; } /** diff --git a/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonSerializer.java b/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonSerializer.java index a00541b61..5674a846a 100644 --- a/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonSerializer.java +++ b/extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonSerializer.java @@ -64,7 +64,7 @@ static ObjectMapper newObjectMapper() { .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // https://github.com/jwtk/jjwt/issues/893 } - protected final ObjectMapper objectMapper; + private final ObjectWriter objectWriter; /** * Constructor using JJWT's default {@link ObjectMapper} singleton for serialization. @@ -80,13 +80,15 @@ public JacksonSerializer() { */ public JacksonSerializer(ObjectMapper objectMapper) { Assert.notNull(objectMapper, "ObjectMapper cannot be null."); - this.objectMapper = objectMapper.registerModule(MODULE); + this.objectWriter = objectMapper + .registerModule(MODULE) + .writer(); } @Override protected void doSerialize(T t, OutputStream out) throws Exception { Assert.notNull(out, "OutputStream cannot be null."); - ObjectWriter writer = this.objectMapper.writer().without(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + ObjectWriter writer = this.objectWriter.without(JsonGenerator.Feature.AUTO_CLOSE_TARGET); writer.writeValue(out, t); } } diff --git a/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonDeserializerTest.groovy b/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonDeserializerTest.groovy index b21667441..58697df75 100644 --- a/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonDeserializerTest.groovy +++ b/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonDeserializerTest.groovy @@ -45,14 +45,14 @@ class JacksonDeserializerTest { @Test void testDefaultConstructor() { - assertSame JacksonSerializer.DEFAULT_OBJECT_MAPPER, deserializer.objectMapper + assertSame JacksonSerializer.DEFAULT_OBJECT_MAPPER.getDeserializationConfig(), deserializer.objectReader.config } @Test void testObjectMapperConstructor() { def customOM = new ObjectMapper() deserializer = new JacksonDeserializer<>(customOM) - assertSame customOM, deserializer.objectMapper + assertSame customOM.getDeserializationConfig(), deserializer.objectReader.config } @Test(expected = IllegalArgumentException) @@ -152,7 +152,7 @@ class JacksonDeserializerTest { */ @Test void testIgnoreUnknownPropertiesWhenDeserializeWithCustomObject() { - + long currentTime = System.currentTimeMillis() String json = """ diff --git a/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonSerializerTest.groovy b/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonSerializerTest.groovy index 0b620ae53..4974d118c 100644 --- a/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonSerializerTest.groovy +++ b/extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonSerializerTest.groovy @@ -16,6 +16,7 @@ package io.jsonwebtoken.jackson.io import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.ObjectWriter import io.jsonwebtoken.io.Serializer import io.jsonwebtoken.lang.Strings import org.junit.Before @@ -47,14 +48,14 @@ class JacksonSerializerTest { @Test void testDefaultConstructor() { - assertSame JacksonSerializer.DEFAULT_OBJECT_MAPPER, ser.objectMapper + assertSame JacksonSerializer.DEFAULT_OBJECT_MAPPER.getSerializationConfig(), ser.objectWriter.config } @Test void testObjectMapperConstructor() { ObjectMapper customOM = new ObjectMapper() ser = new JacksonSerializer(customOM) - assertSame customOM, ser.objectMapper + assertSame customOM.getSerializationConfig(), ser.objectWriter.config } @Test(expected = IllegalArgumentException) @@ -65,8 +66,10 @@ class JacksonSerializerTest { @Test void testObjectMapperConstructorAutoRegistersModule() { ObjectMapper om = createMock(ObjectMapper) + ObjectWriter writer = createMock(ObjectWriter) expect(om.registerModule(same(JacksonSerializer.MODULE))).andReturn(om) - replay om + expect(om.writer()).andReturn(writer) + replay om, writer //noinspection GroovyResultOfObjectAllocationIgnored new JacksonSerializer<>(om) verify om