From 8f0c7c947a4a48d1cc4f5b21fd9a17b004ad3256 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sun, 11 Aug 2019 23:32:35 +0100 Subject: [PATCH] Allow custom serializers to be registered with type aliases for deserializing. --- .../internal/amqp/CustomSerializer.kt | 6 +++++- .../internal/amqp/CustomSerializerRegistry.kt | 15 ++++++++++++--- .../corda/serialization/internal/amqp/Schema.kt | 5 +++-- .../internal/amqp/SerializerFactoryBuilder.kt | 2 +- .../internal/model/LocalTypeModel.kt | 4 +--- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt index 0be0de6c23..ef9134e9e4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt @@ -29,6 +29,10 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { */ open val additionalSerializers: Iterable> = emptyList() + /** + * This custom serializer is also allowed to deserialize these classes. + */ + open val deserializationAliases: Set> = emptySet() protected abstract val descriptor: Descriptor /** @@ -110,7 +114,7 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { */ abstract class CustomSerializerImp(protected val clazz: Class, protected val withInheritance: Boolean) : CustomSerializer() { override val type: Type get() = clazz - override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${AMQPTypeIdentifiers.nameForType(clazz)}") + override val typeDescriptor: Symbol = typeDescriptorFor(clazz) override fun writeClassInfo(output: SerializationOutput) {} override val descriptor: Descriptor = Descriptor(typeDescriptor) override fun isSerializerFor(clazz: Class<*>): Boolean = if (withInheritance) this.clazz.isAssignableFrom(clazz) else this.clazz == clazz diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt index 2081a8162f..48486315fa 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt @@ -84,7 +84,7 @@ class CachingCustomSerializerRegistry( } private val customSerializersCache: MutableMap = DefaultCacheProvider.createCache() - private var customSerializers: List = emptyList() + private val customSerializers: MutableList = mutableListOf() /** * Register a custom serializer for any type that cannot be serialized or deserialized by the default serializer @@ -93,7 +93,7 @@ class CachingCustomSerializerRegistry( override fun register(customSerializer: CustomSerializer) { logger.trace("action=\"Registering custom serializer\", class=\"${customSerializer.type}\"") - if (!customSerializersCache.isEmpty()) { + if (customSerializersCache.isNotEmpty()) { logger.warn("Attempting to register custom serializer $customSerializer.type} in an active cache." + "All serializers should be registered before the cache comes into use.") } @@ -103,14 +103,23 @@ class CachingCustomSerializerRegistry( for (additional in customSerializer.additionalSerializers) { register(additional) } + + for (alias in customSerializer.deserializationAliases) { + val aliasDescriptor = typeDescriptorFor(alias) + if (aliasDescriptor != customSerializer.typeDescriptor) { + descriptorBasedSerializerRegistry[aliasDescriptor.toString()] = customSerializer + } + } + customSerializer } + } override fun registerExternal(customSerializer: CorDappCustomSerializer) { logger.trace("action=\"Registering external serializer\", class=\"${customSerializer.type}\"") - if (!customSerializersCache.isEmpty()) { + if (customSerializersCache.isNotEmpty()) { logger.warn("Attempting to register custom serializer ${customSerializer.type} in an active cache." + "All serializers must be registered before the cache comes into use.") } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt index f1bb00f7c0..81584378a6 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt @@ -9,12 +9,13 @@ import org.apache.qpid.proton.amqp.UnsignedInteger import org.apache.qpid.proton.amqp.UnsignedLong import org.apache.qpid.proton.codec.DescribedTypeConstructor import java.io.NotSerializableException -import net.corda.serialization.internal.carpenter.Field as CarpenterField -import net.corda.serialization.internal.carpenter.Schema as CarpenterSchema +import java.lang.reflect.Type const val DESCRIPTOR_DOMAIN: String = "net.corda" val amqpMagic = CordaSerializationMagic("corda".toByteArray() + byteArrayOf(1, 0)) +fun typeDescriptorFor(type: Type): Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${AMQPTypeIdentifiers.nameForType(type)}") + /** * This and the classes below are OO representations of the AMQP XML schema described in the specification. Their * [toString] representations generate the associated XML form. 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 7c5ec79c49..82de1c6c7c 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 @@ -92,7 +92,7 @@ object SerializerFactoryBuilder { customSerializerRegistry, onlyCustomSerializers) - val typeLoader = ClassCarpentingTypeLoader( + val typeLoader: TypeLoader = ClassCarpentingTypeLoader( SchemaBuildingRemoteTypeCarpenter(classCarpenter), classCarpenter.classloader) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt index 45bdc8794f..fb801c52d7 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt @@ -1,7 +1,5 @@ package net.corda.serialization.internal.model -import net.corda.core.serialization.ClassWhitelist -import net.corda.serialization.internal.amqp.* import java.lang.reflect.* /** @@ -54,7 +52,7 @@ class ConfigurableLocalTypeModel(private val typeModelConfiguration: LocalTypeMo private val typeInformationCache = DefaultCacheProvider.createCache() /** - * We need to provide the [TypeInformationBuilder] with a temporary local cache, so that it doesn't leak + * We need to provide the [LocalTypeInformationBuilder] with a temporary local cache, so that it doesn't leak * [LocalTypeInformation] with unpatched cycles into the global cache where other threads can access them * before we've patched the cycles up. */