diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index 407cac3cf4..f0887e2178 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -397,7 +397,7 @@ fun SerializationContext.withWhitelist(classes: List>): SerializationCo // EXPERIMENTAL @KeepForDJVM -class ExternalSchema() +data class ExternalSchema(val flush: Boolean = false, val externalSchemaForDeserialization: Any? = null, val typeNotations: MutableList> = ArrayList()) @KeepForDJVM class IntegerFingerprints() { diff --git a/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt b/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt index fbaf7594d4..b2045d18c6 100644 --- a/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt +++ b/finance/workflows/src/test/kotlin/net/corda/finance/flows/CompatibilityTest.kt @@ -1,5 +1,6 @@ package net.corda.finance.flows +import net.corda.core.serialization.ExternalSchema import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.SerializedBytes @@ -59,14 +60,15 @@ class CompatibilityTest { assertEquals(1, commands.size) assertTrue(commands.first().value is Cash.Commands.Issue) + val context = SerializationDefaults.STORAGE_CONTEXT.withExternalSchema(ExternalSchema()).withIntegerFingerprint() val newWtx = SerializationFactory.defaultFactory.asCurrent { - withCurrentContext(SerializationDefaults.STORAGE_CONTEXT) { + withCurrentContext(context) { WireTransaction(transaction.tx.componentGroups.map { cg: ComponentGroup -> ComponentGroup(cg.groupIndex, cg.components.map { bytes -> val componentInput = DeserializationInput(serializerFactory) val component = componentInput.deserialize(SerializedBytes(bytes.bytes), SerializationDefaults.STORAGE_CONTEXT) val componentOutput = SerializationOutput(serializerFactory) - val componentOutputBytes = componentOutput.serialize(component, SerializationDefaults.STORAGE_CONTEXT.withIntegerFingerprint()).bytes + val componentOutputBytes = componentOutput.serialize(component, context).bytes OpaqueBytes(componentOutputBytes) }) }) @@ -76,8 +78,8 @@ class CompatibilityTest { // Serialize back and check that representation is byte-to-byte identical to what it was originally. val output = SerializationOutput(serializerFactory) - val outByteArray = output.serialize(newTransaction, SerializationDefaults.STORAGE_CONTEXT.withEncoding(CordaSerializationEncoding.SNAPPY) - .withIntegerFingerprint()).bytes + val outByteArray = output.serialize(newTransaction, context.withExternalSchema(context.externalSchema!!.copy(flush = true)) + .withEncoding(CordaSerializationEncoding.SNAPPY)).bytes //val (serializedBytes, schema) = output.serializeAndReturnSchema(transaction, SerializationDefaults.STORAGE_CONTEXT) println("Output size = ${outByteArray.size}") diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt index 52e9cd847c..6dcbb8e7cd 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/PrimitiveSerializer.kt @@ -2,12 +2,12 @@ package net.corda.serialization.djvm.serializers import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.amqp.AMQPSerializer +import net.corda.serialization.internal.amqp.Descriptor import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.SerializationOutput import net.corda.serialization.internal.amqp.SerializationSchemas import net.corda.serialization.internal.amqp.typeDescriptorFor import org.apache.qpid.proton.amqp.Binary -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type import java.util.function.Function @@ -16,8 +16,7 @@ class PrimitiveSerializer( override val type: Class<*>, private val sandboxBasicInput: Function ) : AMQPSerializer { - override val typeDescriptor: Symbol = typeDescriptorFor(type) - + override val descriptor: Descriptor = Descriptor(typeDescriptorFor(type)) override fun readObject( obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext ): Any { diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt index 5c5fa25c19..60eb5175c6 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt @@ -7,6 +7,7 @@ import net.corda.serialization.djvm.deserializers.CreateCollection import net.corda.serialization.djvm.toSandboxAnyClass import net.corda.serialization.internal.amqp.AMQPSerializer import net.corda.serialization.internal.amqp.CustomSerializer +import net.corda.serialization.internal.amqp.Descriptor import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.LocalSerializerFactory import net.corda.serialization.internal.amqp.Schema @@ -15,7 +16,6 @@ import net.corda.serialization.internal.amqp.SerializationSchemas import net.corda.serialization.internal.amqp.redescribe import net.corda.serialization.internal.model.LocalTypeInformation import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.ParameterizedType import java.lang.reflect.Type @@ -92,18 +92,18 @@ private class ConcreteCollectionSerializer( ) : AMQPSerializer { override val type: ParameterizedType = declaredType - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor( - LocalTypeInformation.ACollection( - observedType = declaredType, - typeIdentifier = TypeIdentifier.forGenericType(declaredType), - elementType = factory.getTypeInformation(declaredType.actualTypeArguments[0]) - ) - ) + override val descriptor: Descriptor by lazy { + Descriptor(factory.createDescriptor( + LocalTypeInformation.ACollection( + observedType = declaredType, + typeIdentifier = TypeIdentifier.forGenericType(declaredType), + elementType = factory.getTypeInformation(declaredType.actualTypeArguments[0]) + ) + )) } override fun readObject( - obj: Any, + obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt index 9420dddf9b..bb97c4d134 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCorDappCustomSerializer.kt @@ -20,7 +20,6 @@ import net.corda.serialization.internal.amqp.SerializationSchemas import net.corda.serialization.internal.amqp.SerializerFactory import net.corda.serialization.internal.amqp.typeDescriptorFor import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.ParameterizedType import java.lang.reflect.Type @@ -65,8 +64,7 @@ class SandboxCorDappCustomSerializer( private val deserializationAlias: TypeIdentifier get() = TypeIdentifier.Erased(AMQPTypeIdentifiers.nameForType(type).replace("sandbox.", ""), 0) - override val typeDescriptor: Symbol = typeDescriptorFor(type) - override val descriptor: Descriptor = Descriptor(typeDescriptor) + override val descriptor: Descriptor = Descriptor(typeDescriptorFor(type)) override val deserializationAliases: Set = singleton(deserializationAlias) /** diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt index 361b467c08..404fc2b442 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxEnumSerializer.kt @@ -9,6 +9,7 @@ import net.corda.serialization.djvm.toSandboxAnyClass import net.corda.serialization.internal.amqp.AMQPNotSerializableException import net.corda.serialization.internal.amqp.AMQPSerializer import net.corda.serialization.internal.amqp.CustomSerializer +import net.corda.serialization.internal.amqp.Descriptor import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.LocalSerializerFactory import net.corda.serialization.internal.amqp.Schema @@ -17,7 +18,6 @@ import net.corda.serialization.internal.amqp.SerializationSchemas import net.corda.serialization.internal.model.EnumTransforms import net.corda.serialization.internal.model.LocalTypeInformation import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type import java.util.function.Function @@ -76,21 +76,21 @@ private class ConcreteEnumSerializer( ) : AMQPSerializer { override val type: Class<*> = declaredType - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor( - /* - * Partially populated, providing just the information - * required by the fingerprinter. - */ - LocalTypeInformation.AnEnum( - declaredType, - TypeIdentifier.forGenericType(declaredType), - memberNames, - emptyMap(), - emptyList(), - EnumTransforms.empty - ) - ) + override val descriptor: Descriptor by lazy { + Descriptor(factory.createDescriptor( + /* + * Partially populated, providing just the information + * required by the fingerprinter. + */ + LocalTypeInformation.AnEnum( + declaredType, + TypeIdentifier.forGenericType(declaredType), + memberNames, + emptyMap(), + emptyList(), + EnumTransforms.empty + ) + )) } override fun readObject( diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt index 6803ff4612..e6f45edc79 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt @@ -6,6 +6,7 @@ import net.corda.serialization.djvm.deserializers.CreateMap import net.corda.serialization.djvm.toSandboxAnyClass import net.corda.serialization.internal.amqp.AMQPSerializer import net.corda.serialization.internal.amqp.CustomSerializer +import net.corda.serialization.internal.amqp.Descriptor import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.LocalSerializerFactory import net.corda.serialization.internal.amqp.Schema @@ -14,7 +15,6 @@ import net.corda.serialization.internal.amqp.SerializationSchemas import net.corda.serialization.internal.amqp.redescribe import net.corda.serialization.internal.model.LocalTypeInformation import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.ParameterizedType import java.lang.reflect.Type @@ -82,15 +82,15 @@ private class ConcreteMapSerializer( ) : AMQPSerializer { override val type: ParameterizedType = declaredType - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor( - LocalTypeInformation.AMap( - observedType = declaredType, - typeIdentifier = TypeIdentifier.forGenericType(declaredType), - keyType = factory.getTypeInformation(declaredType.actualTypeArguments[0]), - valueType = factory.getTypeInformation(declaredType.actualTypeArguments[1]) - ) - ) + override val descriptor: Descriptor by lazy { + Descriptor(factory.createDescriptor( + LocalTypeInformation.AMap( + observedType = declaredType, + typeIdentifier = TypeIdentifier.forGenericType(declaredType), + keyType = factory.getTypeInformation(declaredType.actualTypeArguments[0]), + valueType = factory.getTypeInformation(declaredType.actualTypeArguments[1]) + ) + )) } override fun readObject( diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt index 2a6da2f0bd..761802f183 100644 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeEnumWithEvolutionTest.kt @@ -62,7 +62,7 @@ class DeserializeEnumWithEvolutionTest : TestBase(KOTLIN) { } private fun SerializedBytes<*>.devolve(context: SerializationContext): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this, context.encodingWhitelist).apply { + val envelope = DeserializationInput.getEnvelope(this, context).apply { val schemaTypes = schema.types.map(::devolveType) with(schema.types as MutableList) { clear() diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt index d1b0da91da..978157419d 100644 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeRemoteCustomisedEnumTest.kt @@ -47,10 +47,10 @@ class DeserializeRemoteCustomisedEnumTest : TestBase(KOTLIN) { */ @Suppress("unchecked_cast") private fun SerializedBytes.rewriteEnumAsWorking(): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this).apply { + val envelope = DeserializationInput.getEnvelope(this, AMQP_STORAGE_CONTEXT).apply { val restrictedType = schema.types[0] as RestrictedType (schema.types as MutableList)[0] = restrictedType.copy( - name = toWorking(restrictedType.name) + name = toWorking(restrictedType.name) ) } return SerializedBytes(envelope.write(AMQP_STORAGE_CONTEXT)) @@ -92,15 +92,15 @@ class DeserializeRemoteCustomisedEnumTest : TestBase(KOTLIN) { */ @Suppress("unchecked_cast") private fun SerializedBytes.rewriteContainerAsWorking(): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this).apply { + val envelope = DeserializationInput.getEnvelope(this, AMQP_STORAGE_CONTEXT).apply { val compositeType = schema.types[0] as CompositeType (schema.types as MutableList)[0] = compositeType.copy( - name = toWorking(compositeType.name), - fields = compositeType.fields.map { it.copy(type = toWorking(it.type)) } + name = toWorking(compositeType.name), + fields = compositeType.fields.map { it.copy(type = toWorking(it.type)) } ) val restrictedType = schema.types[1] as RestrictedType (schema.types as MutableList)[1] = restrictedType.copy( - name = toWorking(restrictedType.name) + name = toWorking(restrictedType.name) ) } return SerializedBytes(envelope.write(AMQP_STORAGE_CONTEXT)) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt index 6d851aabd7..b9e27c13c6 100644 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/SafeDeserialisationTest.kt @@ -27,7 +27,7 @@ class SafeDeserialisationTest : TestBase(KOTLIN) { val innocent = InnocentData(MESSAGE, NUMBER) val innocentData = innocent.serialize() - val envelope = DeserializationInput.getEnvelope(innocentData, context.encodingWhitelist).apply { + val envelope = DeserializationInput.getEnvelope(innocentData, context).apply { val innocentType = schema.types[0] as CompositeType (schema.types as MutableList)[0] = innocentType.copy( name = innocentType.name.replace("Innocent", "VeryEvil") diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPDescriptorRegistry.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPDescriptorRegistry.kt index 3cd721bac4..41f80380bf 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPDescriptorRegistry.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPDescriptorRegistry.kt @@ -12,6 +12,9 @@ import org.apache.qpid.proton.amqp.UnsignedLong */ const val DESCRIPTOR_TOP_32BITS: Long = 0xc562L shl (32 + 16) +const val DESCRIPTOR_USER_START: Long = 256L +const val DESCRIPTOR_CUSTOM_SERIALIZER_START: Long = 32L + /** * AMQP descriptor ID's for our custom types. * diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPPrimitiveSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPPrimitiveSerializer.kt index 5342f683a4..446f732442 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPPrimitiveSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPPrimitiveSerializer.kt @@ -12,7 +12,7 @@ import java.lang.reflect.Type * [ByteArray] is automatically marshalled to/from the Proton-J wrapper, [Binary]. */ class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer { - override val typeDescriptor = Symbol.valueOf(AMQPTypeIdentifiers.primitiveTypeName(clazz))!! + override val descriptor: Descriptor = Descriptor(Symbol.valueOf(AMQPTypeIdentifiers.primitiveTypeName(clazz))!!) override val type: Type = clazz // NOOP since this is a primitive type. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt index b4b0b2e58e..f6f3a3115a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt @@ -2,7 +2,6 @@ package net.corda.serialization.internal.amqp import net.corda.core.KeepForDJVM import net.corda.core.serialization.SerializationContext -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -22,7 +21,8 @@ interface AMQPSerializer { * * This should be unique enough that we can use one global cache of [AMQPSerializer]s and use this as the look up key. */ - val typeDescriptor: Symbol + //val typeDescriptor: Symbol + val descriptor: Descriptor /** * Add anything required to the AMQP schema via [SerializationOutput.writeTypeNotations] and any dependent serializers diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt index 0a130f4bcd..a595fc7b31 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt @@ -7,7 +7,6 @@ import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor import net.corda.core.utilities.trace import net.corda.serialization.internal.model.resolveAgainst -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -56,15 +55,13 @@ open class ArraySerializer(override val type: Type, factory: LocalSerializerFact } } - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor(type) - } - internal val elementType: Type by lazy { type.componentType().resolveAgainst(type) } internal open val typeName by lazy { calcTypeName(type) } + override val descriptor: Descriptor by lazy { Descriptor(factory.createDescriptor(type)) } + internal val typeNotation: TypeNotation by lazy { - RestrictedType(typeName, null, emptyList(), "list", Descriptor(typeDescriptor), emptyList()) + RestrictedType(typeName, null, emptyList(), "list", Descriptor(factory.createDescriptor(type)), emptyList()) } override fun writeClassInfo(output: SerializationOutput) { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt index ad48a72749..bd4bbbf196 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt @@ -5,7 +5,6 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.NonEmptySet import net.corda.serialization.internal.model.LocalTypeInformation import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException import java.lang.reflect.ParameterizedType @@ -22,8 +21,8 @@ import java.util.TreeSet class CollectionSerializer(private val declaredType: ParameterizedType, factory: LocalSerializerFactory) : AMQPSerializer { override val type: Type = declaredType - override val typeDescriptor: Symbol by lazy { - factory.createDescriptor(type) + override val descriptor: Descriptor by lazy { + Descriptor(factory.createDescriptor(type)) } companion object { @@ -90,7 +89,7 @@ class CollectionSerializer(private val declaredType: ParameterizedType, factory: private val concreteBuilder: (List<*>) -> Collection<*> = findConcreteType(declaredType.rawType as Class<*>) - private val typeNotation: TypeNotation = RestrictedType(AMQPTypeIdentifiers.nameForType(declaredType), null, emptyList(), "list", Descriptor(typeDescriptor), emptyList()) + private val typeNotation: TypeNotation = RestrictedType(AMQPTypeIdentifiers.nameForType(declaredType), null, emptyList(), "list", descriptor, emptyList()) private val outboundType = resolveTypeVariables(declaredType.actualTypeArguments[0], null) private val inboundType = declaredType.actualTypeArguments[0] diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CorDappCustomSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CorDappCustomSerializer.kt index 5cffc1b1c4..ede9ae4fad 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CorDappCustomSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CorDappCustomSerializer.kt @@ -4,7 +4,6 @@ import com.google.common.reflect.TypeToken import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationCustomSerializer -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type import kotlin.reflect.jvm.javaType @@ -62,8 +61,7 @@ class CorDappCustomSerializer( override val type = types[CORDAPP_TYPE] val proxyType = types[PROXY_TYPE] - override val typeDescriptor: Symbol = typeDescriptorFor(type) - val descriptor: Descriptor = Descriptor(typeDescriptor) + override val descriptor: Descriptor = Descriptor(typeDescriptorFor(type)) private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer.make(factory.getTypeInformation(proxyType), factory) } 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 3b99e64086..bb4dfc4dcc 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 @@ -35,7 +35,6 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { */ open val deserializationAliases: Set = emptySet() - protected abstract val descriptor: Descriptor /** * This exists purely for documentation and cross-platform purposes. It is not used by our serialization / deserialization * code path. @@ -82,12 +81,16 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { override val type: Type get() = clazz - override val typeDescriptor: Symbol by lazy { + override fun writeClassInfo(output: SerializationOutput) { + output.writeTypeNotations(typeNotation) + } + + override val descriptor: Descriptor by lazy { val fingerprint = FingerprintWriter() - .write(superClassSerializer.typeDescriptor) + .write(superClassSerializer.descriptor.name!!) .write(AMQPTypeIdentifiers.nameForType(clazz)) .fingerprint - Symbol.valueOf("$DESCRIPTOR_DOMAIN:$fingerprint") + Descriptor(Symbol.valueOf("$DESCRIPTOR_DOMAIN:$fingerprint")) } private val typeNotation: TypeNotation = RestrictedType( @@ -95,16 +98,10 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { null, emptyList(), AMQPTypeIdentifiers.nameForType(superClassSerializer.type), - Descriptor(typeDescriptor), + descriptor, emptyList() ) - override fun writeClassInfo(output: SerializationOutput) { - output.writeTypeNotations(typeNotation) - } - - override val descriptor: Descriptor = Descriptor(typeDescriptor) - override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput, context: SerializationContext ) { @@ -124,9 +121,8 @@ 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 = typeDescriptorFor(clazz) override fun writeClassInfo(output: SerializationOutput) {} - override val descriptor: Descriptor = Descriptor(typeDescriptor) + override val descriptor: Descriptor = Descriptor(typeDescriptorFor(clazz)) 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 76a99a2bf8..24a3aed9a5 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 @@ -102,16 +102,16 @@ class CachingCustomSerializerRegistry( "All serializers should be registered before the cache comes into use.") } - descriptorBasedSerializerRegistry.getOrBuild(customSerializer.typeDescriptor.toString()) { + descriptorBasedSerializerRegistry.getOrBuild(customSerializer.descriptor.name.toString()) { customSerializers += customSerializer for (additional in customSerializer.additionalSerializers) { register(additional) } for (alias in customSerializer.deserializationAliases) { - val aliasDescriptor = typeDescriptorFor(alias) - if (aliasDescriptor != customSerializer.typeDescriptor) { - descriptorBasedSerializerRegistry[aliasDescriptor.toString()] = customSerializer + val aliasDescriptor = Descriptor(typeDescriptorFor(alias)) + if (aliasDescriptor != customSerializer.descriptor) { + descriptorBasedSerializerRegistry[aliasDescriptor.name.toString()] = customSerializer } } @@ -128,7 +128,7 @@ class CachingCustomSerializerRegistry( "All serializers must be registered before the cache comes into use.") } - descriptorBasedSerializerRegistry.getOrBuild(customSerializer.typeDescriptor.toString()) { + descriptorBasedSerializerRegistry.getOrBuild(customSerializer.descriptor.name.toString()) { customSerializers += customSerializer customSerializer } 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 7be1425d32..3457be810b 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 @@ -2,8 +2,8 @@ package net.corda.serialization.internal.amqp import net.corda.core.KeepForDJVM import net.corda.core.internal.VisibleForTesting -import net.corda.core.serialization.EncodingWhitelist import net.corda.core.serialization.AMQP_ENVELOPE_CACHE_PROPERTY +import net.corda.core.serialization.EncodingWhitelist import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.ByteSequence @@ -11,7 +11,6 @@ import net.corda.core.utilities.loggerFor import net.corda.core.utilities.trace import net.corda.serialization.internal.ByteBufferInputStream import net.corda.serialization.internal.CordaSerializationEncoding -import net.corda.serialization.internal.NullEncodingWhitelist import net.corda.serialization.internal.SectionId import net.corda.serialization.internal.encodingNotPermittedFormat import net.corda.serialization.internal.model.TypeIdentifier @@ -21,7 +20,6 @@ import org.apache.qpid.proton.amqp.UnsignedInteger import org.apache.qpid.proton.codec.Data import java.io.InputStream import java.io.NotSerializableException -import java.lang.Exception import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.lang.reflect.TypeVariable @@ -73,8 +71,8 @@ class DeserializationInput constructor( } @Throws(AMQPNoTypeNotSerializableException::class) - fun getEnvelope(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist): Envelope { - return withDataBytes(byteSequence, encodingWhitelist) { dataBytes -> + fun getEnvelope(byteSequence: ByteSequence, context: SerializationContext): Envelope { + return withDataBytes(byteSequence, context.encodingWhitelist) { dataBytes -> val data = Data.Factory.create() val expectedSize = dataBytes.remaining() if (data.decode(dataBytes) != expectedSize.toLong()) { @@ -82,15 +80,18 @@ class DeserializationInput constructor( "Unexpected size of data", "Blob is corrupted!.") } - Envelope.get(data) + val envelope = Envelope.get(data) + if (context.externalSchema == null || envelope.schema.types.isNotEmpty()) { + envelope + } else { + Envelope(envelope.obj, context.externalSchema?.externalSchemaForDeserialization as? Schema + ?: Schema.newInstance(listOf(context.externalSchema?.typeNotations?.flatten() + ?.toList() as List)), TransformsSchema.newInstance(null)) + } } } } - @VisibleForTesting - @Throws(AMQPNoTypeNotSerializableException::class) - fun getEnvelope(byteSequence: ByteSequence, context: SerializationContext) = getEnvelope(byteSequence, context.encodingWhitelist) - @Throws( AMQPNotSerializableException::class, AMQPNoTypeNotSerializableException::class) @@ -133,9 +134,9 @@ class DeserializationInput constructor( */ @Suppress("unchecked_cast") val envelope = (context.properties[AMQP_ENVELOPE_CACHE_PROPERTY] as? MutableMap) - ?.computeIfAbsent(IdentityKey(bytes)) { key -> - getEnvelope(key.bytes, context.encodingWhitelist) - } ?: getEnvelope(bytes, context.encodingWhitelist) + ?.computeIfAbsent(IdentityKey(bytes)) { key -> + getEnvelope(key.bytes, context) + } ?: getEnvelope(bytes, context) logger.trace { "deserialize blob scheme=\"${envelope.schema}\"" } @@ -148,7 +149,7 @@ class DeserializationInput constructor( clazz: Class, context: SerializationContext ): ObjectAndEnvelope = des { - val envelope = getEnvelope(bytes, context.encodingWhitelist) + val envelope = getEnvelope(bytes, context) // Now pick out the obj and schema from the envelope. ObjectAndEnvelope(doReadObject(envelope, clazz, context), envelope) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumEvolutionSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumEvolutionSerializer.kt index 77b57f9235..4539b8babb 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumEvolutionSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumEvolutionSerializer.kt @@ -3,7 +3,6 @@ package net.corda.serialization.internal.amqp import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.model.BaseLocalTypes import org.apache.qpid.proton.codec.Data -import java.lang.UnsupportedOperationException import java.lang.reflect.Type /** @@ -38,7 +37,7 @@ class EnumEvolutionSerializer( private val baseLocalTypes: BaseLocalTypes, private val conversions: Map, private val ordinals: Map) : AMQPSerializer { - override val typeDescriptor = factory.createDescriptor(type) + override val descriptor: Descriptor = Descriptor(factory.createDescriptor(type)) override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumSerializer.kt index ede6a00e28..d7101c1a40 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumSerializer.kt @@ -11,13 +11,13 @@ import java.lang.reflect.Type class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: LocalSerializerFactory) : AMQPSerializer { override val type: Type = declaredType private val typeNotation: TypeNotation - override val typeDescriptor = factory.createDescriptor(type) + override val descriptor: Descriptor = Descriptor(factory.createDescriptor(type)) init { @Suppress("unchecked_cast") typeNotation = RestrictedType( AMQPTypeIdentifiers.nameForType(declaredType), - null, emptyList(), "list", Descriptor(typeDescriptor), + null, emptyList(), "list", descriptor, (declaredClass as Class>).enumConstants.zip(IntRange(0, declaredClass.enumConstants.size)).map { Choice(it.first.name, it.second.toString()) }) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt index 7e9ac86068..c03dbf4356 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt @@ -19,6 +19,7 @@ data class Envelope(val obj: Any?, val schema: Schema, val transformsSchema: Tra val DESCRIPTOR_OBJECT = Descriptor(null, DESCRIPTOR) // described list should either be two or three elements long + private const val ENVELOPE_WITHOUT_SCHEMA = 1 private const val ENVELOPE_WITHOUT_TRANSFORMS = 2 private const val ENVELOPE_WITH_TRANSFORMS = 3 private const val ENVELOPE_WITH_TRANSFORMS_AND_EXTERNAL_SCHEMA = 3 @@ -36,27 +37,39 @@ data class Envelope(val obj: Any?, val schema: Schema, val transformsSchema: Tra } val list = describedType.described as List<*> + // We need to cope with objects serialised without the schema header element in the + // envelope + val schema: Any? = when (list.size) { + ENVELOPE_WITHOUT_SCHEMA -> Schema(emptyList()) + ENVELOPE_WITHOUT_TRANSFORMS -> Schema.get(list[SCHEMA_IDX]!!) + ENVELOPE_WITH_TRANSFORMS -> Schema.get(list[SCHEMA_IDX]!!) + ENVELOPE_WITH_TRANSFORMS_AND_EXTERNAL_SCHEMA -> Schema.get(list[SCHEMA_IDX]!!) + else -> throw AMQPNoTypeNotSerializableException( + "Malformed list, bad length of ${list.size} (should be 1, 2, 3 or 4)") + } // We need to cope with objects serialised without the transforms header element in the // envelope val transformSchema: Any? = when (list.size) { + ENVELOPE_WITHOUT_SCHEMA -> null ENVELOPE_WITHOUT_TRANSFORMS -> null ENVELOPE_WITH_TRANSFORMS -> list[TRANSFORMS_SCHEMA_IDX] ENVELOPE_WITH_TRANSFORMS_AND_EXTERNAL_SCHEMA -> list[TRANSFORMS_SCHEMA_IDX] else -> throw AMQPNoTypeNotSerializableException( - "Malformed list, bad length of ${list.size} (should be 2, 3 or 4)") + "Malformed list, bad length of ${list.size} (should be 1, 2, 3 or 4)") } - +/* // We need to cope with objects serialised without the external schema header element in the // envelope val externalSchema: Any? = when (list.size) { + ENVELOPE_WITHOUT_SCHEMA -> null ENVELOPE_WITHOUT_TRANSFORMS -> null ENVELOPE_WITH_TRANSFORMS -> null ENVELOPE_WITH_TRANSFORMS_AND_EXTERNAL_SCHEMA -> list[EXTERNAL_SCHEMA_IDX] else -> throw AMQPNoTypeNotSerializableException( - "Malformed list, bad length of ${list.size} (should be 2, 3 or 4)") - } + "Malformed list, bad length of ${list.size} (should be 1, 2, 3 or 4)") + }*/ - return newInstance(listOf(list[BLOB_IDX], Schema.get(list[SCHEMA_IDX]!!), + return newInstance(listOf(list[BLOB_IDX], schema, TransformsSchema.newInstance(transformSchema))) } 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 9b0ce7b9ae..8c2354d213 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 @@ -5,12 +5,16 @@ import net.corda.core.serialization.ClassWhitelist import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.core.utilities.trace -import net.corda.serialization.internal.model.* -import net.corda.serialization.internal.model.TypeIdentifier.* +import net.corda.serialization.internal.model.DefaultCacheProvider +import net.corda.serialization.internal.model.FingerPrinter +import net.corda.serialization.internal.model.LocalTypeInformation +import net.corda.serialization.internal.model.LocalTypeModel +import net.corda.serialization.internal.model.TypeIdentifier +import net.corda.serialization.internal.model.TypeIdentifier.Parameterised import org.apache.qpid.proton.amqp.Symbol import java.lang.reflect.ParameterizedType import java.lang.reflect.Type -import java.util.* +import java.util.Optional import java.util.function.Function import java.util.function.Predicate import javax.annotation.concurrent.ThreadSafe @@ -146,7 +150,7 @@ class DefaultLocalSerializerFactory( private fun makeAndCache(typeIdentifier: TypeIdentifier, build: () -> AMQPSerializer) = serializersByTypeId.getOrPut(typeIdentifier) { build().also { serializer -> - descriptorBasedSerializerRegistry[serializer.typeDescriptor.toString()] = serializer + descriptorBasedSerializerRegistry[serializer.descriptor.name.toString()] = serializer } } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt index 90db74e936..97a46ffb6d 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt @@ -6,7 +6,6 @@ import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.model.LocalTypeInformation import net.corda.serialization.internal.model.TypeIdentifier -import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException import java.lang.reflect.ParameterizedType @@ -29,7 +28,7 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> class MapSerializer(private val declaredType: ParameterizedType, factory: LocalSerializerFactory) : AMQPSerializer { override val type: Type = declaredType - override val typeDescriptor: Symbol = factory.createDescriptor(type) + override val descriptor: Descriptor = Descriptor(factory.createDescriptor(type)) companion object { // NB: Order matters in this map, the most specific classes should be listed at the end @@ -90,7 +89,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: LocalS private val concreteBuilder: MapCreationFunction = findConcreteType(declaredType.rawType as Class<*>) - private val typeNotation: TypeNotation = RestrictedType(AMQPTypeIdentifiers.nameForType(declaredType), null, emptyList(), "map", Descriptor(typeDescriptor), emptyList()) + private val typeNotation: TypeNotation = RestrictedType(AMQPTypeIdentifiers.nameForType(declaredType), null, emptyList(), "map", descriptor, emptyList()) private val inboundKeyType = declaredType.actualTypeArguments[0] private val outboundKeyType = resolveTypeVariables(inboundKeyType, null) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt index 3ead63df50..f5d2c3106c 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt @@ -102,12 +102,14 @@ interface ObjectSerializer : AMQPSerializer { class ComposableObjectSerializer( override val type: Type, - override val typeDescriptor: Symbol, + typeDescriptor: Symbol, override val propertySerializers: Map, override val fields: List, private val reader: ComposableObjectReader, private val writer: ComposableObjectWriter): ObjectSerializer { + override val descriptor: Descriptor = Descriptor(typeDescriptor) + override fun writeClassInfo(output: SerializationOutput) = writer.writeClassInfo(output) override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int) = @@ -178,26 +180,30 @@ class ComposableObjectReader( class AbstractObjectSerializer( override val type: Type, - override val typeDescriptor: Symbol, + typeDescriptor: Symbol, override val propertySerializers: Map, override val fields: List, private val writer: ComposableObjectWriter): ObjectSerializer { + + override val descriptor: Descriptor = Descriptor(typeDescriptor) override fun writeClassInfo(output: SerializationOutput) = - writer.writeClassInfo(output) + writer.writeClassInfo(output) override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int) = - writer.writeObject(obj, data, type, output, context, debugIndent) + writer.writeObject(obj, data, type, output, context, debugIndent) override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any = - throw UnsupportedOperationException("Cannot deserialize abstract type ${type.typeName}") + throw UnsupportedOperationException("Cannot deserialize abstract type ${type.typeName}") } class EvolutionObjectSerializer( override val type: Type, - override val typeDescriptor: Symbol, + typeDescriptor: Symbol, override val propertySerializers: Map, private val reader: ComposableObjectReader): ObjectSerializer { + override val descriptor: Descriptor = Descriptor(typeDescriptor) + companion object { fun make(localTypeInformation: LocalTypeInformation.Composable, remoteTypeInformation: RemoteTypeInformation.Composable, diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt index 355827f1d9..75d439d468 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/RemoteSerializerFactory.kt @@ -97,7 +97,7 @@ class DefaultRemoteSerializerFactory( // Obtain a serializer and descriptor for the local type. val localSerializer = localSerializerFactory.get(localTypeInformation) - val localDescriptor = localSerializer.typeDescriptor.toString() + val localDescriptor = localSerializer.descriptor.name.toString() return when { // If descriptors match, we can return the local serializer straight away. 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 1e8e748bee..d119a0abd7 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 @@ -35,7 +35,7 @@ fun Descriptor.maybeConvertToInteger(context: SerializationContext): Descriptor val descriptorMappings = context.integerFingerprints?.descriptorMappings if (descriptorMappings == null) return this return context.integerFingerprints!!.descriptorMappings.computeIfAbsent(this) { - Descriptor(null, UnsignedLong(32L + descriptorMappings.size or DESCRIPTOR_TOP_32BITS)) + Descriptor(null, UnsignedLong(DESCRIPTOR_USER_START + descriptorMappings.size or DESCRIPTOR_TOP_32BITS)) } as Descriptor } 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 0fa90915b6..e6786ccd52 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 @@ -80,9 +80,22 @@ open class SerializationOutput constructor( data.withDescribed(Envelope.DESCRIPTOR_OBJECT, context) { withList { writeObject(obj, this, context) - val schema = Schema(schemaHistory.map { it.maybeConvertDescriptorToInteger(context) }.toList()) - writeSchema(schema, this) - writeTransformSchema(TransformsSchema.build(schema, serializerFactory), this) + if (context.externalSchema == null) { + val types = schemaHistory.map { it.maybeConvertDescriptorToInteger(context) }.toList() + val schema = Schema(types) + writeSchema(schema, this) + writeTransformSchema(TransformsSchema.build(schema, serializerFactory), this) + } else { + context.externalSchema?.typeNotations?.add(schemaHistory.map { it.maybeConvertDescriptorToInteger(context) }.toList()) + if (context.externalSchema!!.flush) { + val descriptors: MutableSet = HashSet() + val schema = Schema((context.externalSchema?.typeNotations?.flatten()?.toList() as List).filter { + descriptors.add(it.descriptor) + }) + writeSchema(schema, this) + writeTransformSchema(TransformsSchema.build(schema, serializerFactory), this) + } + } } } return SerializedBytes(byteArrayOutput { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SingletonSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SingletonSerializer.kt index 2eb7630031..51f836e8be 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SingletonSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SingletonSerializer.kt @@ -11,13 +11,13 @@ import java.lang.reflect.Type * want converting back to that singleton instance on the receiving JVM. */ class SingletonSerializer(override val type: Class<*>, val singleton: Any, factory: LocalSerializerFactory) : AMQPSerializer { - override val typeDescriptor = factory.createDescriptor(type) + override val descriptor: Descriptor = Descriptor(factory.createDescriptor(type)) private val interfaces = (factory.getTypeInformation(type) as LocalTypeInformation.Singleton).interfaces private fun generateProvides(): List = interfaces.map { it.typeIdentifier.name } - internal val typeNotation: TypeNotation = RestrictedType(type.typeName, "Singleton", generateProvides(), "boolean", Descriptor(typeDescriptor), emptyList()) + internal val typeNotation: TypeNotation = RestrictedType(type.typeName, "Singleton", generateProvides(), "boolean", descriptor, emptyList()) override fun writeClassInfo(output: SerializationOutput) { output.writeTypeNotations(typeNotation) 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 8965a5c8e1..8b47e2ff6a 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 @@ -246,9 +246,9 @@ private class FingerPrintingState( } val customTypeDescriptor = customSerializerRegistry.findCustomSerializer( - clazz = observedClass, - declaredType = observedGenericType - )?.typeDescriptor?.toString() + clazz = observedClass, + declaredType = observedGenericType + )?.descriptor?.name?.toString() if (customTypeDescriptor != null) writer.write(customTypeDescriptor) else defaultAction() } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationCompatibilityTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationCompatibilityTests.kt index f27d877774..6fc61663b7 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationCompatibilityTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/SerializationCompatibilityTests.kt @@ -9,6 +9,6 @@ class SerializationCompatibilityTests { @Test(timeout=300_000) fun `fingerprint is stable`() { val factory = testDefaultFactoryNoEvolution().apply { register(ThrowableSerializer(this)) } - assertThat(factory.get(Exception::class.java).typeDescriptor.toString()).isEqualTo("net.corda:ApZ2a/36VVskaoDZMbiZ8A==") + assertThat(factory.get(Exception::class.java).descriptor.name.toString()).isEqualTo("net.corda:ApZ2a/36VVskaoDZMbiZ8A==") } } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumToStringFallbackTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumToStringFallbackTest.kt index 6389f78591..108e0d375f 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumToStringFallbackTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumToStringFallbackTest.kt @@ -3,6 +3,7 @@ package net.corda.serialization.internal.amqp import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext.UseCase.Testing import net.corda.core.serialization.SerializedBytes +import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.SerializationContextImpl import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput @@ -51,15 +52,15 @@ class EnumToStringFallbackTest { */ @Suppress("unchecked_cast") private fun SerializedBytes.rewriteAsWorking(): SerializedBytes { - val envelope = DeserializationInput.getEnvelope(this).apply { + val envelope = DeserializationInput.getEnvelope(this, AMQP_STORAGE_CONTEXT).apply { val compositeType = schema.types[0] as CompositeType (schema.types as MutableList)[0] = compositeType.copy( - name = toWorking(compositeType.name), - fields = compositeType.fields.map { it.copy(type = toWorking(it.type)) } + name = toWorking(compositeType.name), + fields = compositeType.fields.map { it.copy(type = toWorking(it.type)) } ) val restrictedType = schema.types[1] as RestrictedType (schema.types as MutableList)[1] = restrictedType.copy( - name = toWorking(restrictedType.name) + name = toWorking(restrictedType.name) ) } return SerializedBytes(envelope.write()) diff --git a/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt b/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt index 00b29d5ff5..674e74fdbf 100644 --- a/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt +++ b/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt @@ -22,7 +22,9 @@ import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.amqpMagic -import picocli.CommandLine.* +import picocli.CommandLine.ITypeConverter +import picocli.CommandLine.Option +import picocli.CommandLine.Parameters import java.io.PrintStream import java.net.MalformedURLException import java.net.URL @@ -59,7 +61,7 @@ class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised initialiseSerialization() if (schema) { - val envelope = DeserializationInput.getEnvelope(bytes.sequence(), SerializationDefaults.STORAGE_CONTEXT.encodingWhitelist) + val envelope = DeserializationInput.getEnvelope(bytes.sequence(), SerializationDefaults.STORAGE_CONTEXT) out.println(envelope.schema) out.println() out.println(envelope.transformsSchema)