Skip to content

Commit

Permalink
ValueCreator modified to cache to KotlinValueInstantiator
Browse files Browse the repository at this point in the history
The following benefits can be obtained
- Throughput is increased because searches are no longer performed in the reflection cache
- Tuning is simplified because memory is released based on memory usage
  • Loading branch information
k163377 committed Aug 5, 2023
1 parent 043e89e commit 0a20f65
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ package io.github.projectmapk.jackson.module.kogera

import com.fasterxml.jackson.databind.introspect.AnnotatedMethod
import com.fasterxml.jackson.databind.util.LRUMap
import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ConstructorValueCreator
import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.MethodValueCreator
import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ValueCreator
import io.github.projectmapk.jackson.module.kogera.ser.ValueClassBoxConverter
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) : Serializable {
Expand All @@ -21,7 +15,6 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {

// This cache is used for both serialization and deserialization, so reserve a larger size from the start.
private val classCache = LRUMap<Class<*>, JmClass>(reflectionCacheSize, reflectionCacheSize)
private val creatorCache: LRUMap<Executable, ValueCreator<*>>

// Initial size is 0 because the value class is not always used
private val valueClassReturnTypeCache: LRUMap<AnnotatedMethod, Optional<Class<*>>> =
Expand All @@ -32,21 +25,6 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
private val valueClassBoxConverterCache: LRUMap<Class<*>, ValueClassBoxConverter<*, *>> =
LRUMap(0, reflectionCacheSize)

init {
// The current default value of reflectionCacheSize is 512.
// If less than 512 is specified, initialEntries shall be half of the reflectionCacheSize,
// since it is assumed that the situation does not require a large amount of space from the beginning.
// Conversely, if reflectionCacheSize is increased,
// the amount of cache is considered to be a performance bottleneck,
// and therefore reflectionCacheSize is used as is for initialEntries.
val initialEntries = if (reflectionCacheSize <= KotlinModule.Builder.reflectionCacheSizeDefault) {
reflectionCacheSize / 2
} else {
reflectionCacheSize
}
creatorCache = LRUMap(initialEntries, reflectionCacheSize)
}

fun getJmClass(clazz: Class<*>): JmClass? {
return classCache[clazz] ?: run {
val kmClass = clazz.toKmClass() ?: return null
Expand All @@ -66,35 +44,6 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
}
}

/**
* return null if declaringClass is not kotlin class
*/
fun valueCreatorFromJava(creator: Executable): ValueCreator<*>? = when (creator) {
is Constructor<*> -> {
creatorCache.get(creator)
?: run {
getJmClass(creator.declaringClass)?.let {
val value = ConstructorValueCreator(creator, it)
creatorCache.putIfAbsent(creator, value) ?: value
}
}
}

is Method -> {
creatorCache.get(creator)
?: run {
getJmClass(creator.declaringClass)?.let {
val value = MethodValueCreator<Any?>(creator, it)
creatorCache.putIfAbsent(creator, value) ?: value
}
}
}

else -> throw IllegalStateException(
"Expected a constructor or method to create a Kotlin object, instead found ${creator.javaClass.name}"
)
} // we cannot reflect this method so do the default Java-ish behavior

private fun AnnotatedMethod.getValueClassReturnType(): Class<*>? {
val getter = this.member.apply {
// If the return value of the getter is a value class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator
import com.fasterxml.jackson.databind.exc.MismatchedInputException
import io.github.projectmapk.jackson.module.kogera.ReflectionCache
import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ConstructorValueCreator
import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.MethodValueCreator
import io.github.projectmapk.jackson.module.kogera.deser.value_instantiator.creator.ValueCreator
import java.lang.reflect.Constructor
import java.lang.reflect.Executable
import java.lang.reflect.Method

private fun JsonMappingException.wrapWithPath(refFrom: Any?, refFieldName: String) =
JsonMappingException.wrapWithPath(this, refFrom, refFieldName)
Expand All @@ -29,13 +33,25 @@ internal class KotlinValueInstantiator(
private fun JavaType.requireEmptyValue() =
(nullToEmptyCollection && this.isCollectionLikeType) || (nullToEmptyMap && this.isMapLikeType)

private val valueCreator: ValueCreator<*>? by ReflectProperties.lazySoft {
val creator = _withArgsCreator.annotated as Executable
val jmClass = cache.getJmClass(creator.declaringClass) ?: return@lazySoft null

when (creator) {
is Constructor<*> -> ConstructorValueCreator(creator, jmClass)
is Method -> MethodValueCreator<Any?>(creator, jmClass)
else -> throw IllegalStateException(
"Expected a constructor or method to create a Kotlin object, instead found ${creator.javaClass.name}"
)
}
} // we cannot reflect this method so do the default Java-ish behavior

override fun createFromObjectWith(
ctxt: DeserializationContext,
props: Array<out SettableBeanProperty>,
buffer: PropertyValueBuffer
): Any? {
val valueCreator: ValueCreator<*> = cache.valueCreatorFromJava(_withArgsCreator.annotated as Executable)
?: return super.createFromObjectWith(ctxt, props, buffer)
val valueCreator: ValueCreator<*> = valueCreator ?: return super.createFromObjectWith(ctxt, props, buffer)

val bucket = valueCreator.generateBucket()

Expand Down

0 comments on commit 0a20f65

Please sign in to comment.