Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ReflectionCache to be serializable #78

Merged
merged 2 commits into from
Feb 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import com.fasterxml.jackson.module.kotlin.deser.value_instantiator.creator.Meth
import com.fasterxml.jackson.module.kotlin.deser.value_instantiator.creator.ValueCreator
import com.fasterxml.jackson.module.kotlin.ser.ValueClassBoxConverter
import kotlinx.metadata.KmClass
import java.io.Serializable
import java.lang.reflect.Constructor
import java.lang.reflect.Executable
import java.lang.reflect.Method
import java.util.Optional

internal class ReflectionCache(reflectionCacheSize: Int) {
internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
companion object {
// Increment is required when properties that use LRUMap are changed.
private const val serialVersionUID = 1L
}

// This cache is used for both serialization and deserialization, so reserve a larger size from the start.
private val classCache = LRUMap<Class<*>, Optional<KmClass>>(reflectionCacheSize, reflectionCacheSize)
private val creatorCache: LRUMap<Executable, ValueCreator<*>>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.fasterxml.jackson.module.kotlin

import org.junit.jupiter.api.Assertions.fail
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 <T> jdkDeserialize(raw: ByteArray): T {
val objIn = ObjectInputStream(ByteArrayInputStream(raw))
return try {
@Suppress("UNCHECKED_CAST")
objIn.readObject() as T
} catch (e: ClassNotFoundException) {
fail("Missing class: " + e.message)
} finally {
objIn.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.fasterxml.jackson.module.kotlin

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow

class KotlinModuleTest {
@Test
fun jdkSerializabilityTest() {
val module = KotlinModule.Builder().apply {
withReflectionCacheSize(123)
enable(KotlinFeature.NullToEmptyCollection)
enable(KotlinFeature.NullToEmptyMap)
enable(KotlinFeature.NullIsSameAsDefault)
enable(KotlinFeature.SingletonSupport)
enable(KotlinFeature.StrictNullChecks)
}.build()

val serialized = jdkSerialize(module)

assertDoesNotThrow {
val deserialized = jdkDeserialize<KotlinModule>(serialized)

assertNotNull(deserialized)
assertEquals(123, deserialized.reflectionCacheSize)
assertTrue(deserialized.nullToEmptyCollection)
assertTrue(deserialized.nullToEmptyMap)
assertTrue(deserialized.nullIsSameAsDefault)
assertTrue(deserialized.singletonSupport)
assertTrue(deserialized.strictNullChecks)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.fasterxml.jackson.module.kotlin

import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow

class ReflectionCacheTest {
@Nested
inner class JDKSerializabilityTest {
@Test
fun emptyCache() {
val cache = ReflectionCache(100)
val serialized = jdkSerialize(cache)

assertDoesNotThrow {
val deserialized = jdkDeserialize<ReflectionCache>(serialized)

assertNotNull(deserialized)
// Deserialized instance also do not raise exceptions
deserialized.getKmClass(this::class.java)
}
}

@Test
fun notEmptyCache() {
val cache = ReflectionCache(100).apply { getKmClass(this::class.java) }
val serialized = jdkSerialize(cache)

assertDoesNotThrow {
val deserialized = jdkDeserialize<ReflectionCache>(serialized)

assertNotNull(deserialized)
// Deserialized instance also do not raise exceptions
deserialized.getKmClass(this::class.java)
}
}
}
}