CORDA-3218: Make set of serializer types considered suitable for object reference to be configurable.

This commit is contained in:
Chris Rankin 2019-09-12 15:50:13 +01:00
parent dea00fd107
commit 7af8011649
6 changed files with 25 additions and 11 deletions

View File

@ -188,7 +188,7 @@ class DeserializationInput constructor(
// Store the reference in case we need it later on. // Store the reference in case we need it later on.
// Skip for primitive types as they are too small and overhead of referencing them will be much higher // Skip for primitive types as they are too small and overhead of referencing them will be much higher
// than their content // than their content
if (suitableForObjectReference(objectRead.javaClass)) { if (serializerFactory.isSuitebleForObjectReference(objectRead.javaClass)) {
objectHistory.add(objectRead) objectHistory.add(objectRead)
} }
objectRead objectRead

View File

@ -13,6 +13,7 @@ import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type import java.lang.reflect.Type
import java.util.* import java.util.*
import java.util.function.Function import java.util.function.Function
import java.util.function.Predicate
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
/** /**
@ -77,6 +78,12 @@ interface LocalSerializerFactory {
* Use the [FingerPrinter] to create a type descriptor for the given [typeInformation]. * Use the [FingerPrinter] to create a type descriptor for the given [typeInformation].
*/ */
fun createDescriptor(typeInformation: LocalTypeInformation): Symbol fun createDescriptor(typeInformation: LocalTypeInformation): Symbol
/**
* Determines whether instances of this type should be added to the object history
* when serialising and deserialising.
*/
fun isSuitebleForObjectReference(type: Type): Boolean
} }
/** /**
@ -91,6 +98,7 @@ class DefaultLocalSerializerFactory(
override val classloader: ClassLoader, override val classloader: ClassLoader,
private val descriptorBasedSerializerRegistry: DescriptorBasedSerializerRegistry, private val descriptorBasedSerializerRegistry: DescriptorBasedSerializerRegistry,
private val primitiveSerializerFactory: Function<Class<*>, AMQPSerializer<Any>>, private val primitiveSerializerFactory: Function<Class<*>, AMQPSerializer<Any>>,
private val isPrimitiveType: Predicate<Class<*>>,
private val customSerializerRegistry: CustomSerializerRegistry, private val customSerializerRegistry: CustomSerializerRegistry,
private val onlyCustomSerializers: Boolean) private val onlyCustomSerializers: Boolean)
: LocalSerializerFactory { : LocalSerializerFactory {
@ -127,6 +135,12 @@ class DefaultLocalSerializerFactory(
override fun get(typeInformation: LocalTypeInformation): AMQPSerializer<Any> = override fun get(typeInformation: LocalTypeInformation): AMQPSerializer<Any> =
get(typeInformation.observedType, typeInformation) get(typeInformation.observedType, typeInformation)
// ByteArrays, primitives and boxed primitives are not stored in the object history
override fun isSuitebleForObjectReference(type: Type): Boolean {
val clazz = type.asClass()
return type != ByteArray::class.java && !isPrimitiveType.test(clazz)
}
private fun makeAndCache(typeInformation: LocalTypeInformation, build: () -> AMQPSerializer<Any>) = private fun makeAndCache(typeInformation: LocalTypeInformation, build: () -> AMQPSerializer<Any>) =
makeAndCache(typeInformation.typeIdentifier, build) makeAndCache(typeInformation.typeIdentifier, build)
@ -144,7 +158,9 @@ class DefaultLocalSerializerFactory(
// Any Custom Serializer cached for a ParameterizedType can only be // Any Custom Serializer cached for a ParameterizedType can only be
// found by searching for that exact same type. Searching for its raw // found by searching for that exact same type. Searching for its raw
// class will not work! // class will not work!
val declaredGenericType = if (declaredType !is ParameterizedType && localTypeInformation.typeIdentifier is Parameterised) { val declaredGenericType = if (declaredType !is ParameterizedType
&& localTypeInformation.typeIdentifier is Parameterised
&& declaredClass != Class::class.java) {
localTypeInformation.typeIdentifier.getLocalType(classLoaderFor(declaredClass)) localTypeInformation.typeIdentifier.getLocalType(classLoaderFor(declaredClass))
} else { } else {
declaredType declaredType

View File

@ -1,6 +1,5 @@
package net.corda.serialization.internal.amqp package net.corda.serialization.internal.amqp
import com.google.common.primitives.Primitives
import com.google.common.reflect.TypeToken import com.google.common.reflect.TypeToken
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.serialization.internal.model.TypeIdentifier import net.corda.serialization.internal.model.TypeIdentifier
@ -109,12 +108,6 @@ internal fun Type.isSubClassOf(type: Type): Boolean {
return TypeToken.of(this).isSubtypeOf(TypeToken.of(type).rawType) return TypeToken.of(this).isSubtypeOf(TypeToken.of(type).rawType)
} }
// ByteArrays, primitives and boxed primitives are not stored in the object history
internal fun suitableForObjectReference(type: Type): Boolean {
val clazz = type.asClass()
return type != ByteArray::class.java && (!clazz.isPrimitive && !Primitives.unwrap(clazz).isPrimitive)
}
/** /**
* Common properties that are to be used in the [SerializationContext.properties] to alter serialization behavior/content * Common properties that are to be used in the [SerializationContext.properties] to alter serialization behavior/content
*/ */

View File

@ -137,7 +137,7 @@ open class SerializationOutput constructor(
// Important to do it after serialization such that dependent object will have preceding reference numbers // Important to do it after serialization such that dependent object will have preceding reference numbers
// assigned to them first as they will be first read from the stream on receiving end. // assigned to them first as they will be first read from the stream on receiving end.
// Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content // Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content
if (suitableForObjectReference(obj.javaClass)) { if (serializerFactory.isSuitebleForObjectReference(obj.javaClass)) {
objectHistory[obj] = objectHistory.size objectHistory[obj] = objectHistory.size
} }
} else { } else {

View File

@ -1,5 +1,6 @@
package net.corda.serialization.internal.amqp package net.corda.serialization.internal.amqp
import com.google.common.primitives.Primitives
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.ClassWhitelist
@ -9,6 +10,7 @@ import net.corda.serialization.internal.model.*
import java.io.NotSerializableException import java.io.NotSerializableException
import java.util.Collections.unmodifiableMap import java.util.Collections.unmodifiableMap
import java.util.function.Function import java.util.function.Function
import java.util.function.Predicate
@KeepForDJVM @KeepForDJVM
object SerializerFactoryBuilder { object SerializerFactoryBuilder {
@ -109,6 +111,7 @@ object SerializerFactoryBuilder {
classCarpenter.classloader, classCarpenter.classloader,
descriptorBasedSerializerRegistry, descriptorBasedSerializerRegistry,
Function { clazz -> AMQPPrimitiveSerializer(clazz) }, Function { clazz -> AMQPPrimitiveSerializer(clazz) },
Predicate { clazz -> clazz.isPrimitive || Primitives.unwrap(clazz).isPrimitive },
customSerializerRegistry, customSerializerRegistry,
onlyCustomSerializers) onlyCustomSerializers)

View File

@ -236,7 +236,9 @@ private class FingerPrintingState(
// Any Custom Serializer cached for a ParameterizedType can only be // Any Custom Serializer cached for a ParameterizedType can only be
// found by searching for that exact same type. Searching for its raw // found by searching for that exact same type. Searching for its raw
// class will not work! // class will not work!
val observedGenericType = if (observedType !is ParameterizedType && type.typeIdentifier is Parameterised) { val observedGenericType = if (observedType !is ParameterizedType
&& type.typeIdentifier is Parameterised
&& observedClass != Class::class.java) {
type.typeIdentifier.getLocalType(classLoaderFor(observedClass)) type.typeIdentifier.getLocalType(classLoaderFor(observedClass))
} else { } else {
observedType observedType