From 7af80116492b45f27ac3718ba59c3de17e7c5481 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 12 Sep 2019 15:50:13 +0100 Subject: [PATCH] CORDA-3218: Make set of serializer types considered suitable for object reference to be configurable. --- .../internal/amqp/DeserializationInput.kt | 2 +- .../internal/amqp/LocalSerializerFactory.kt | 18 +++++++++++++++++- .../internal/amqp/SerializationHelper.kt | 7 ------- .../internal/amqp/SerializationOutput.kt | 2 +- .../internal/amqp/SerializerFactoryBuilder.kt | 3 +++ .../model/TypeModellingFingerPrinter.kt | 4 +++- 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt index c21050bc8d..70d0432193 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt @@ -188,7 +188,7 @@ class DeserializationInput constructor( // 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 // than their content - if (suitableForObjectReference(objectRead.javaClass)) { + if (serializerFactory.isSuitebleForObjectReference(objectRead.javaClass)) { objectHistory.add(objectRead) } objectRead diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt index bfd8863441..3673c04620 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/LocalSerializerFactory.kt @@ -13,6 +13,7 @@ import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.* import java.util.function.Function +import java.util.function.Predicate import javax.annotation.concurrent.ThreadSafe /** @@ -77,6 +78,12 @@ interface LocalSerializerFactory { * Use the [FingerPrinter] to create a type descriptor for the given [typeInformation]. */ 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, private val descriptorBasedSerializerRegistry: DescriptorBasedSerializerRegistry, private val primitiveSerializerFactory: Function, AMQPSerializer>, + private val isPrimitiveType: Predicate>, private val customSerializerRegistry: CustomSerializerRegistry, private val onlyCustomSerializers: Boolean) : LocalSerializerFactory { @@ -127,6 +135,12 @@ class DefaultLocalSerializerFactory( override fun get(typeInformation: LocalTypeInformation): AMQPSerializer = 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) = makeAndCache(typeInformation.typeIdentifier, build) @@ -144,7 +158,9 @@ class DefaultLocalSerializerFactory( // Any Custom Serializer cached for a ParameterizedType can only be // found by searching for that exact same type. Searching for its raw // 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)) } else { declaredType diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt index eff270e249..21c5d0bf19 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt @@ -1,6 +1,5 @@ package net.corda.serialization.internal.amqp -import com.google.common.primitives.Primitives import com.google.common.reflect.TypeToken import net.corda.core.serialization.* 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) } -// 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 */ diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt index 1ba283203f..1d5b45b1d6 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt @@ -137,7 +137,7 @@ open class SerializationOutput constructor( // 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. // 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 } } else { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt index a6469b443c..a038af6fe2 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactoryBuilder.kt @@ -1,5 +1,6 @@ package net.corda.serialization.internal.amqp +import com.google.common.primitives.Primitives import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.serialization.ClassWhitelist @@ -9,6 +10,7 @@ import net.corda.serialization.internal.model.* import java.io.NotSerializableException import java.util.Collections.unmodifiableMap import java.util.function.Function +import java.util.function.Predicate @KeepForDJVM object SerializerFactoryBuilder { @@ -109,6 +111,7 @@ object SerializerFactoryBuilder { classCarpenter.classloader, descriptorBasedSerializerRegistry, Function { clazz -> AMQPPrimitiveSerializer(clazz) }, + Predicate { clazz -> clazz.isPrimitive || Primitives.unwrap(clazz).isPrimitive }, customSerializerRegistry, onlyCustomSerializers) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt index b0ac855d34..c5d79ed41f 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt @@ -236,7 +236,9 @@ private class FingerPrintingState( // Any Custom Serializer cached for a ParameterizedType can only be // found by searching for that exact same type. Searching for its raw // 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)) } else { observedType