ENT-2977 resolve custom serializers earlier (#4609)

* ENT-2977 resolve custom serializers earlier

* Remove unnecessary import

* Cache hot serialisation paths

* Remove blank line
This commit is contained in:
Dominic Fox 2019-01-24 11:31:51 +00:00 committed by GitHub
parent 9f4c8bcea5
commit d540aa5b17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 56 deletions

View File

@ -27,7 +27,18 @@ class CachingCustomSerializerRegistry(
private data class CustomSerializerIdentifier(val actualTypeIdentifier: TypeIdentifier, val declaredTypeIdentifier: TypeIdentifier)
private val customSerializersCache: MutableMap<CustomSerializerIdentifier, AMQPSerializer<Any>> = DefaultCacheProvider.createCache()
private sealed class CustomSerializerLookupResult {
abstract val serializerIfFound: AMQPSerializer<Any>?
object None : CustomSerializerLookupResult() {
override val serializerIfFound: AMQPSerializer<Any>? = null
}
data class CustomSerializerFound(override val serializerIfFound: AMQPSerializer<Any>) : CustomSerializerLookupResult()
}
private val customSerializersCache: MutableMap<CustomSerializerIdentifier, CustomSerializerLookupResult> = DefaultCacheProvider.createCache()
private var customSerializers: List<SerializerFor> = emptyList()
/**
@ -37,6 +48,11 @@ class CachingCustomSerializerRegistry(
override fun register(customSerializer: CustomSerializer<out Any>) {
logger.trace("action=\"Registering custom serializer\", class=\"${customSerializer.type}\"")
if (!customSerializersCache.isEmpty()) {
logger.warn("Attempting to register custom serializer $customSerializer.type} in an active cache." +
"All serializers should be registered before the cache comes into use.")
}
descriptorBasedSerializerRegistry.getOrBuild(customSerializer.typeDescriptor.toString()) {
customSerializers += customSerializer
for (additional in customSerializer.additionalSerializers) {
@ -49,6 +65,11 @@ class CachingCustomSerializerRegistry(
override fun registerExternal(customSerializer: CorDappCustomSerializer) {
logger.trace("action=\"Registering external serializer\", class=\"${customSerializer.type}\"")
if (!customSerializersCache.isEmpty()) {
logger.warn("Attempting to register custom serializer ${customSerializer.type} in an active cache." +
"All serializers must be registered before the cache comes into use.")
}
descriptorBasedSerializerRegistry.getOrBuild(customSerializer.typeDescriptor.toString()) {
customSerializers += customSerializer
customSerializer
@ -60,10 +81,11 @@ class CachingCustomSerializerRegistry(
TypeIdentifier.forClass(clazz),
TypeIdentifier.forGenericType(declaredType))
return customSerializersCache[typeIdentifier]
?: doFindCustomSerializer(clazz, declaredType)?.also { serializer ->
customSerializersCache.putIfAbsent(typeIdentifier, serializer)
}
return customSerializersCache.getOrPut(typeIdentifier) {
val customSerializer = doFindCustomSerializer(clazz, declaredType)
if (customSerializer == null) CustomSerializerLookupResult.None
else CustomSerializerLookupResult.CustomSerializerFound(customSerializer)
}.serializerIfFound
}
private fun doFindCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer<Any>? {

View File

@ -7,10 +7,8 @@ import net.corda.core.utilities.debug
import net.corda.core.utilities.trace
import net.corda.serialization.internal.model.*
import org.apache.qpid.proton.amqp.Symbol
import java.io.NotSerializableException
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.lang.reflect.WildcardType
import java.util.*
import javax.annotation.concurrent.ThreadSafe
@ -90,7 +88,10 @@ class DefaultLocalSerializerFactory(
val logger = contextLogger()
}
private val serializersByType: MutableMap<TypeIdentifier, AMQPSerializer<Any>> = DefaultCacheProvider.createCache()
private data class ActualAndDeclaredType(val actualType: Class<*>, val declaredType: Type)
private val serializersByActualAndDeclaredType: MutableMap<ActualAndDeclaredType, AMQPSerializer<Any>> = DefaultCacheProvider.createCache()
private val serializersByTypeId: MutableMap<TypeIdentifier, AMQPSerializer<Any>> = DefaultCacheProvider.createCache()
private val typesByName = DefaultCacheProvider.createCache<String, Optional<LocalTypeInformation>>()
override fun createDescriptor(typeInformation: LocalTypeInformation): Symbol =
@ -101,10 +102,10 @@ class DefaultLocalSerializerFactory(
override fun getTypeInformation(typeName: String): LocalTypeInformation? {
return typesByName.getOrPut(typeName) {
val localType = try {
Class.forName(typeName, false, classloader)
} catch (_: ClassNotFoundException) {
null
}
Class.forName(typeName, false, classloader)
} catch (_: ClassNotFoundException) {
null
}
Optional.ofNullable(localType?.run { getTypeInformation(this) })
}.orElse(null)
}
@ -112,73 +113,85 @@ class DefaultLocalSerializerFactory(
override fun get(typeInformation: LocalTypeInformation): AMQPSerializer<Any> =
get(typeInformation.observedType, typeInformation)
private fun make(typeInformation: LocalTypeInformation, build: () -> AMQPSerializer<Any>) =
make(typeInformation.typeIdentifier, build)
private fun makeAndCache(typeInformation: LocalTypeInformation, build: () -> AMQPSerializer<Any>) =
makeAndCache(typeInformation.typeIdentifier, build)
private fun make(typeIdentifier: TypeIdentifier, build: () -> AMQPSerializer<Any>) =
serializersByType.computeIfAbsent(typeIdentifier) { _ -> build() }
private fun get(declaredType: Type, localTypeInformation: LocalTypeInformation): AMQPSerializer<Any> {
val declaredClass = declaredType.asClass()
// can be useful to enable but will be *extremely* chatty if you do
logger.trace { "Get Serializer for $declaredClass ${declaredType.typeName}" }
return when(localTypeInformation) {
is LocalTypeInformation.ACollection -> makeDeclaredCollection(localTypeInformation)
is LocalTypeInformation.AMap -> makeDeclaredMap(localTypeInformation)
is LocalTypeInformation.AnEnum -> makeDeclaredEnum(localTypeInformation, declaredType, declaredClass)
else -> makeClassSerializer(declaredClass, declaredType, declaredType, localTypeInformation)
}.also { serializer -> descriptorBasedSerializerRegistry[serializer.typeDescriptor.toString()] = serializer }
}
private fun makeDeclaredEnum(localTypeInformation: LocalTypeInformation, declaredType: Type, declaredClass: Class<*>): AMQPSerializer<Any> =
make(localTypeInformation) {
whitelist.requireWhitelisted(declaredType)
EnumSerializer(declaredType, declaredClass, this)
private fun makeAndCache(typeIdentifier: TypeIdentifier, build: () -> AMQPSerializer<Any>) =
serializersByTypeId.getOrPut(typeIdentifier) {
build().also { serializer ->
descriptorBasedSerializerRegistry[serializer.typeDescriptor.toString()] = serializer
}
}
private fun get(declaredType: Type, localTypeInformation: LocalTypeInformation): AMQPSerializer<Any> =
serializersByTypeId.getOrPut(localTypeInformation.typeIdentifier) {
val declaredClass = declaredType.asClass()
// can be useful to enable but will be *extremely* chatty if you do
logger.trace { "Get Serializer for $declaredClass ${declaredType.typeName}" }
customSerializerRegistry.findCustomSerializer(declaredClass, declaredType)?.apply { return@get this }
return when (localTypeInformation) {
is LocalTypeInformation.ACollection -> makeDeclaredCollection(localTypeInformation)
is LocalTypeInformation.AMap -> makeDeclaredMap(localTypeInformation)
is LocalTypeInformation.AnEnum -> makeDeclaredEnum(localTypeInformation, declaredType, declaredClass)
else -> makeClassSerializer(declaredClass, declaredType, localTypeInformation)
}
}
private fun makeDeclaredEnum(localTypeInformation: LocalTypeInformation, declaredType: Type, declaredClass: Class<*>): AMQPSerializer<Any> =
makeAndCache(localTypeInformation) {
whitelist.requireWhitelisted(declaredType)
EnumSerializer(declaredType, declaredClass, this)
}
private fun makeActualEnum(localTypeInformation: LocalTypeInformation, declaredType: Type, declaredClass: Class<*>): AMQPSerializer<Any> =
make(localTypeInformation) {
makeAndCache(localTypeInformation) {
whitelist.requireWhitelisted(declaredType)
EnumSerializer(declaredType, declaredClass, this)
}
private fun makeDeclaredCollection(localTypeInformation: LocalTypeInformation.ACollection): AMQPSerializer<Any> {
val resolved = CollectionSerializer.resolveDeclared(localTypeInformation)
return make(resolved) {
return makeAndCache(resolved) {
CollectionSerializer(resolved.typeIdentifier.getLocalType(classloader) as ParameterizedType, this)
}
}
private fun makeDeclaredMap(localTypeInformation: LocalTypeInformation.AMap): AMQPSerializer<Any> {
val resolved = MapSerializer.resolveDeclared(localTypeInformation)
return make(resolved) {
return makeAndCache(resolved) {
MapSerializer(resolved.typeIdentifier.getLocalType(classloader) as ParameterizedType, this)
}
}
override fun get(actualClass: Class<*>, declaredType: Type): AMQPSerializer<Any> {
// can be useful to enable but will be *extremely* chatty if you do
logger.trace { "Get Serializer for $actualClass ${declaredType.typeName}" }
val actualAndDeclaredType = ActualAndDeclaredType(actualClass, declaredType)
return serializersByActualAndDeclaredType.getOrPut(actualAndDeclaredType) {
// can be useful to enable but will be *extremely* chatty if you do
logger.trace { "Get Serializer for $actualClass ${declaredType.typeName}" }
customSerializerRegistry.findCustomSerializer(actualClass, declaredType)?.apply { return@get this }
val declaredClass = declaredType.asClass()
val actualType: Type = inferTypeVariables(actualClass, declaredClass, declaredType) ?: declaredType
val declaredTypeInformation = typeModel.inspect(declaredType)
val actualTypeInformation = typeModel.inspect(actualType)
val declaredClass = declaredType.asClass()
val actualType: Type = inferTypeVariables(actualClass, declaredClass, declaredType) ?: declaredType
val declaredTypeInformation = typeModel.inspect(declaredType)
val actualTypeInformation = typeModel.inspect(actualType)
return when(actualTypeInformation) {
is LocalTypeInformation.ACollection -> makeActualCollection(actualClass,declaredTypeInformation as? LocalTypeInformation.ACollection ?: actualTypeInformation)
is LocalTypeInformation.AMap -> makeActualMap(declaredType, actualClass,declaredTypeInformation as? LocalTypeInformation.AMap ?: actualTypeInformation)
is LocalTypeInformation.AnEnum -> makeActualEnum(actualTypeInformation, actualType, actualClass)
else -> makeClassSerializer(actualClass, actualType, declaredType, actualTypeInformation)
}.also { serializer -> descriptorBasedSerializerRegistry[serializer.typeDescriptor.toString()] = serializer }
return when (actualTypeInformation) {
is LocalTypeInformation.ACollection -> makeActualCollection(actualClass, declaredTypeInformation as? LocalTypeInformation.ACollection
?: actualTypeInformation)
is LocalTypeInformation.AMap -> makeActualMap(declaredType, actualClass, declaredTypeInformation as? LocalTypeInformation.AMap
?: actualTypeInformation)
is LocalTypeInformation.AnEnum -> makeActualEnum(actualTypeInformation, actualType, actualClass)
else -> makeClassSerializer(actualClass, actualType, actualTypeInformation)
}
}
}
private fun makeActualMap(declaredType: Type, actualClass: Class<*>, typeInformation: LocalTypeInformation.AMap): AMQPSerializer<Any> {
declaredType.asClass().checkSupportedMapType()
val resolved = MapSerializer.resolveActual(actualClass, typeInformation)
return make(resolved) {
return makeAndCache(resolved) {
MapSerializer(resolved.typeIdentifier.getLocalType(classloader) as ParameterizedType, this)
}
}
@ -186,7 +199,7 @@ class DefaultLocalSerializerFactory(
private fun makeActualCollection(actualClass: Class<*>, typeInformation: LocalTypeInformation.ACollection): AMQPSerializer<Any> {
val resolved = CollectionSerializer.resolveActual(actualClass, typeInformation)
return serializersByType.computeIfAbsent(resolved.typeIdentifier) {
return makeAndCache(resolved) {
CollectionSerializer(resolved.typeIdentifier.getLocalType(classloader) as ParameterizedType, this)
}
}
@ -194,9 +207,8 @@ class DefaultLocalSerializerFactory(
private fun makeClassSerializer(
clazz: Class<*>,
type: Type,
declaredType: Type,
typeInformation: LocalTypeInformation
): AMQPSerializer<Any> = make(typeInformation) {
): AMQPSerializer<Any> = makeAndCache(typeInformation) {
logger.debug { "class=${clazz.simpleName}, type=$type is a composite type" }
when {
clazz.isSynthetic -> // Explicitly ban synthetic classes, we have no way of recreating them when deserializing. This also
@ -205,8 +217,7 @@ class DefaultLocalSerializerFactory(
type,
"Serializer does not support synthetic classes")
AMQPTypeIdentifiers.isPrimitive(typeInformation.typeIdentifier) -> AMQPPrimitiveSerializer(clazz)
else -> customSerializerRegistry.findCustomSerializer(clazz, declaredType) ?:
makeNonCustomSerializer(type, typeInformation, clazz)
else -> makeNonCustomSerializer(type, typeInformation, clazz)
}
}