Change descriptor name from string to symbol on the wire to adhere to section 1.5 of the AMQP 1.0 spec (#1423)

This commit is contained in:
Rick Parker 2017-09-07 17:05:39 +01:00 committed by GitHub
parent 879b1a6393
commit 6bf2871819
30 changed files with 36 additions and 29 deletions

View File

@ -27,7 +27,7 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) {
abstract class AbstractAMQPSerializationScheme : SerializationScheme {
internal companion object {
private val pluginRegistries: List<CordaPluginRegistry> by lazy {
ServiceLoader.load(CordaPluginRegistry::class.java, this.javaClass.classLoader).toList()
ServiceLoader.load(CordaPluginRegistry::class.java, this::class.java.classLoader).toList()
}
fun registerCustomSerializers(factory: SerializerFactory) {

View File

@ -1,6 +1,7 @@
package net.corda.nodeapi.internal.serialization.amqp
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
@ -10,7 +11,7 @@ import java.lang.reflect.Type
* [ByteArray] is automatically marshalled to/from the Proton-J wrapper, [Binary].
*/
class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer<Any> {
override val typeDescriptor: String = SerializerFactory.primitiveTypeName(clazz)!!
override val typeDescriptor = Symbol.valueOf(SerializerFactory.primitiveTypeName(clazz)!!)
override val type: Type = clazz
// NOOP since this is a primitive type.

View File

@ -1,5 +1,6 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -18,7 +19,7 @@ interface AMQPSerializer<out T> {
*
* 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: String
val typeDescriptor: Symbol
/**
* Add anything required to the AMQP schema via [SerializationOutput.writeTypeNotations] and any dependent serializers

View File

@ -1,5 +1,6 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.Type
@ -31,12 +32,12 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory)
"${type.componentType().typeName}$arrayType"
}
override val typeDescriptor by lazy { "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}" }
override val typeDescriptor by lazy { Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") }
internal val elementType: Type by lazy { type.componentType() }
internal open val typeName by lazy { calcTypeName(type) }
internal val typeNotation: TypeNotation by lazy {
RestrictedType(typeName, null, emptyList(), "list", Descriptor(typeDescriptor, null), emptyList())
RestrictedType(typeName, null, emptyList(), "list", Descriptor(typeDescriptor), emptyList())
}
override fun writeClassInfo(output: SerializationOutput) {

View File

@ -1,6 +1,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.utilities.NonEmptySet
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.ParameterizedType
@ -15,7 +16,7 @@ import kotlin.collections.Set
*/
class CollectionSerializer(val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType))
override val typeDescriptor = "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}"
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
companion object {
// NB: Order matters in this map, the most specific classes should be listed at the end
@ -58,7 +59,7 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali
private val concreteBuilder: (List<*>) -> Collection<*> = findConcreteType(declaredType.rawType as Class<*>)
private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(declaredType), null, emptyList(), "list", Descriptor(typeDescriptor, null), emptyList())
private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(declaredType), null, emptyList(), "list", Descriptor(typeDescriptor), emptyList())
override fun writeClassInfo(output: SerializationOutput) {
if (output.writeTypeNotations(typeNotation)) {

View File

@ -1,6 +1,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -49,8 +50,8 @@ abstract class CustomSerializer<T> : AMQPSerializer<T> {
override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == this.clazz
override val type: Type get() = clazz
override val typeDescriptor: String = "$DESCRIPTOR_DOMAIN:${fingerprintForDescriptors(superClassSerializer.typeDescriptor, nameForType(clazz))}"
private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(clazz), null, emptyList(), SerializerFactory.nameForType(superClassSerializer.type), Descriptor(typeDescriptor, null), emptyList())
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForDescriptors(superClassSerializer.typeDescriptor.toString(), nameForType(clazz))}")
private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(clazz), null, emptyList(), SerializerFactory.nameForType(superClassSerializer.type), Descriptor(typeDescriptor), emptyList())
override fun writeClassInfo(output: SerializationOutput) {
output.writeTypeNotations(typeNotation)
}
@ -72,7 +73,7 @@ abstract class CustomSerializer<T> : AMQPSerializer<T> {
*/
abstract class CustomSerializerImp<T>(protected val clazz: Class<T>, protected val withInheritance: Boolean) : CustomSerializer<T>() {
override val type: Type get() = clazz
override val typeDescriptor: String = "$DESCRIPTOR_DOMAIN:${nameForType(clazz)}"
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(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

View File

@ -1,8 +1,9 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.io.NotSerializableException
import java.lang.reflect.Type
/**
* Our definition of an enum with the AMQP spec is a list (of two items, a string and an int) that is
@ -10,13 +11,13 @@ import java.io.NotSerializableException
*/
class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: SerializerFactory) : AMQPSerializer<Any> {
override val type: Type = declaredType
override val typeDescriptor = "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}"
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
private val typeNotation: TypeNotation
init {
typeNotation = RestrictedType(
SerializerFactory.nameForType(declaredType),
null, emptyList(), "list", Descriptor(typeDescriptor, null),
null, emptyList(), "list", Descriptor(typeDescriptor),
declaredClass.enumConstants.zip(IntRange(0, declaredClass.enumConstants.size)).map {
Choice(it.first.toString(), it.second.toString())
})

View File

@ -1,5 +1,6 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.ParameterizedType
@ -17,7 +18,7 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *>
*/
class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType))
override val typeDescriptor = "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}"
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
companion object {
// NB: Order matters in this map, the most specific classes should be listed at the end
@ -60,7 +61,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial
private val concreteBuilder: MapCreationFunction = findConcreteType(declaredType.rawType as Class<*>)
private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(declaredType), null, emptyList(), "map", Descriptor(typeDescriptor, null), emptyList())
private val typeNotation: TypeNotation = RestrictedType(SerializerFactory.nameForType(declaredType), null, emptyList(), "map", Descriptor(typeDescriptor), emptyList())
override fun writeClassInfo(output: SerializationOutput) {
if (output.writeTypeNotations(typeNotation)) {

View File

@ -3,6 +3,7 @@ package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.Type
@ -24,10 +25,10 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
private val typeName = nameForType(clazz)
override val typeDescriptor = "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}"
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
private val interfaces = interfacesForSerialization(clazz, factory) // We restrict to only those annotated or whitelisted
open internal val typeNotation : TypeNotation by lazy {CompositeType(typeName, null, generateProvides(), Descriptor(typeDescriptor, null), generateFields()) }
open internal val typeNotation: TypeNotation by lazy { CompositeType(typeName, null, generateProvides(), Descriptor(typeDescriptor), generateFields()) }
override fun writeClassInfo(output: SerializationOutput) {
if (output.writeTypeNotations(typeNotation)) {

View File

@ -2,10 +2,11 @@ package net.corda.nodeapi.internal.serialization.amqp
import com.google.common.hash.Hasher
import com.google.common.hash.Hashing
import net.corda.core.utilities.toBase64
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.toBase64
import org.apache.qpid.proton.amqp.DescribedType
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.amqp.UnsignedInteger
import org.apache.qpid.proton.amqp.UnsignedLong
import org.apache.qpid.proton.codec.Data
@ -13,7 +14,6 @@ import org.apache.qpid.proton.codec.DescribedTypeConstructor
import java.io.NotSerializableException
import java.lang.reflect.*
import java.util.*
import net.corda.nodeapi.internal.serialization.carpenter.Field as CarpenterField
import net.corda.nodeapi.internal.serialization.carpenter.Schema as CarpenterSchema
@ -106,7 +106,9 @@ data class Schema(val types: List<TypeNotation>) : DescribedType {
override fun toString(): String = types.joinToString("\n")
}
data class Descriptor(val name: String?, val code: UnsignedLong? = null) : DescribedType {
data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : DescribedType {
constructor(name: String?) : this(Symbol.valueOf(name))
companion object : DescribedTypeConstructor<Descriptor> {
val DESCRIPTOR = DescriptorRegistry.OBJECT_DESCRIPTOR.amqpDescriptor
@ -122,7 +124,7 @@ data class Descriptor(val name: String?, val code: UnsignedLong? = null) : Descr
override fun newInstance(described: Any?): Descriptor {
val list = described as? List<*> ?: throw IllegalStateException("Was expecting a list")
return Descriptor(list[0] as? String, list[1] as? UnsignedLong)
return Descriptor(list[0] as? Symbol, list[1] as? UnsignedLong)
}
}

View File

@ -18,16 +18,12 @@ data class schemaAndDescriptor(val schema: Schema, val typeDescriptor: Any)
/**
* Factory of serializers designed to be shared across threads and invocations.
*/
// TODO: object references - need better fingerprinting?
// TODO: class references? (e.g. cheat with repeated descriptors using a long encoding, like object ref proposal)
// TODO: Inner classes etc. Should we allow? Currently not considered.
// TODO: support for intern-ing of deserialized objects for some core types (e.g. PublicKey) for memory efficiency
// TODO: maybe support for caching of serialized form of some core types for performance
// TODO: profile for performance in general
// TODO: use guava caches etc so not unbounded
// TODO: do we need to support a transient annotation to exclude certain properties?
// TODO: allow definition of well known types that are left out of the schema.
// TODO: found a document that states textual descriptors are Symbols. Adjust schema class appropriately.
// TODO: document and alert to the fact that classes cannot default superclass/interface properties otherwise they are "erased" due to matching with constructor.
// TODO: type name prefixes for interfaces and abstract classes? Or use label?
// TODO: generic types should define restricted type alias with source of the wildcarded version, I think, if we're to generate classes from schema

View File

@ -1,5 +1,6 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -9,12 +10,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: SerializerFactory) : AMQPSerializer<Any> {
override val typeDescriptor = "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}"
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
private val interfaces = interfacesForSerialization(type, factory)
private fun generateProvides(): List<String> = interfaces.map { it.typeName }
internal val typeNotation: TypeNotation = RestrictedType(type.typeName, "Singleton", generateProvides(), "boolean", Descriptor(typeDescriptor, null), emptyList())
internal val typeNotation: TypeNotation = RestrictedType(type.typeName, "Singleton", generateProvides(), "boolean", Descriptor(typeDescriptor), emptyList())
override fun writeClassInfo(output: SerializationOutput) {
output.writeTypeNotations(typeNotation)

View File

@ -1,8 +1,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
import net.corda.core.serialization.SerializedBytes
import org.junit.Test
import java.io.File
import java.io.NotSerializableException