From 3212e7c9c6969815abc66b633771707986b65287 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Feb 2023 01:14:19 +0900 Subject: [PATCH 1/4] Fix ReflectionCache to be serializable Fixes #295 --- .../com/fasterxml/jackson/module/kotlin/ReflectionCache.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt index 97a925a5..13bf9049 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt @@ -5,13 +5,18 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedMember import com.fasterxml.jackson.databind.introspect.AnnotatedMethod import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams import com.fasterxml.jackson.databind.util.LRUMap +import java.io.Serializable import java.lang.reflect.Constructor import java.lang.reflect.Executable import java.lang.reflect.Method import kotlin.reflect.KFunction import kotlin.reflect.jvm.kotlinFunction -internal class ReflectionCache(reflectionCacheSize: Int) { +internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { + companion object { + private const val serialVersionUID = 1L + } + sealed class BooleanTriState(val value: Boolean?) { class True : BooleanTriState(true) class False : BooleanTriState(false) From d75498cc99f9a276994d314ac909d3e5bfb4a3ad Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Thu, 23 Feb 2023 01:40:11 +0900 Subject: [PATCH 2/4] Add comment --- .../com/fasterxml/jackson/module/kotlin/ReflectionCache.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt index 13bf9049..f229cec8 100644 --- a/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt +++ b/src/main/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCache.kt @@ -14,6 +14,7 @@ import kotlin.reflect.jvm.kotlinFunction internal class ReflectionCache(reflectionCacheSize: Int) : Serializable { companion object { + // Increment is required when properties that use LRUMap are changed. private const val serialVersionUID = 1L } From c135316150f2ed5572f112d90169ece4d96bdd9e Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 25 Feb 2023 14:32:53 +0900 Subject: [PATCH 3/4] Add JDK serializability test for ReflectionCache --- .../kotlin/JDKSerializabilityTestHelper.kt | 28 +++++++++++++++++ .../module/kotlin/ReflectionCacheTest.kt | 30 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/JDKSerializabilityTestHelper.kt create mode 100644 src/test/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheTest.kt diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/JDKSerializabilityTestHelper.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/JDKSerializabilityTestHelper.kt new file mode 100644 index 00000000..a5af1dbd --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/JDKSerializabilityTestHelper.kt @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.module.kotlin + +import junit.framework.TestCase +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream + +fun jdkSerialize(o: Any): ByteArray { + val bytes = ByteArrayOutputStream(1000) + val obOut = ObjectOutputStream(bytes) + obOut.writeObject(o) + obOut.close() + return bytes.toByteArray() +} + +fun jdkDeserialize(raw: ByteArray): T? { + val objIn = ObjectInputStream(ByteArrayInputStream(raw)) + return try { + @Suppress("UNCHECKED_CAST") + objIn.readObject() as T + } catch (e: ClassNotFoundException) { + TestCase.fail("Missing class: " + e.message) + null + } finally { + objIn.close() + } +} diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheTest.kt new file mode 100644 index 00000000..7914a402 --- /dev/null +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/ReflectionCacheTest.kt @@ -0,0 +1,30 @@ +package com.fasterxml.jackson.module.kotlin + +import org.junit.Test +import kotlin.test.assertNotNull + +class ReflectionCacheTest { + @Test + fun serializeEmptyCache() { + val cache = ReflectionCache(100) + val serialized = jdkSerialize(cache) + val deserialized = jdkDeserialize(serialized) + + assertNotNull(deserialized) + // Deserialized instance also do not raise exceptions + deserialized.kotlinFromJava(ReflectionCacheTest::class.java.getDeclaredMethod("serializeEmptyCache")) + } + + @Test + fun serializeNotEmptyCache() { + val method = ReflectionCacheTest::class.java.getDeclaredMethod("serializeNotEmptyCache") + + val cache = ReflectionCache(100).apply { kotlinFromJava(method) } + val serialized = jdkSerialize(cache) + val deserialized = jdkDeserialize(serialized) + + assertNotNull(deserialized) + // Deserialized instance also do not raise exceptions + deserialized.kotlinFromJava(method) + } +} From c6ee34cb1325f1de2f14c81c10bebf6db20878b9 Mon Sep 17 00:00:00 2001 From: wrongwrong Date: Sat, 25 Feb 2023 14:36:50 +0900 Subject: [PATCH 4/4] Add JDK serializability test for KotlinModule --- .../jackson/module/kotlin/KotlinModuleTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModuleTest.kt b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModuleTest.kt index fe2e07cd..6968c835 100644 --- a/src/test/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModuleTest.kt +++ b/src/test/kotlin/com/fasterxml/jackson/module/kotlin/KotlinModuleTest.kt @@ -11,6 +11,7 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test +import kotlin.test.assertNotNull class KotlinModuleTest { /** @@ -103,4 +104,27 @@ class KotlinModuleTest { assertTrue(module.strictNullChecks) } + + @Test + fun jdkSerializabilityTest() { + val module = KotlinModule.Builder().apply { + withReflectionCacheSize(123) + enable(NullToEmptyCollection) + enable(NullToEmptyMap) + enable(NullIsSameAsDefault) + enable(SingletonSupport) + enable(StrictNullChecks) + }.build() + + val serialized = jdkSerialize(module) + val deserialized = jdkDeserialize(serialized) + + assertNotNull(deserialized) + assertEquals(123, deserialized.reflectionCacheSize) + assertTrue(deserialized.nullToEmptyCollection) + assertTrue(deserialized.nullToEmptyMap) + assertTrue(deserialized.nullIsSameAsDefault) + assertEquals(CANONICALIZE, deserialized.singletonSupport) + assertTrue(deserialized.strictNullChecks) + } }