CORDA-786 - Use reflection to infer proxy and proxied types in CorDapp custom serializers

This removes any need for the user implement and override types from the
super class

    * CORDA-786 - Docs update
    * CORDA-786 - Remove unneeded second annotation on the proxy objects
    * Fix merge conflicts
This commit is contained in:
Katelyn Baker
2017-12-04 13:10:02 +00:00
parent d25b7f560c
commit fcec60e232
18 changed files with 80 additions and 110 deletions

View File

@ -5,24 +5,70 @@ import net.corda.core.serialization.SerializationCustomSerializer
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
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.jvmErasure
/**
* Index into the types list of the parent type of the serializer object, should be the
* type that this object proxies for
*/
const val CORDAPP_TYPE = 0
/**
* Index into the types list of the parent type of the serializer object, should be the
* type of the proxy object that we're using to represent the object we're proxying for
*/
const val PROXY_TYPE = 1
/**
* Wrapper class for user provided serializers
*
* Through the CorDapp JAR scanner we will have a list of custom serializer types that implement
* the toProxy and fromProxy methods. This class takes an instance of one of those objects and
* embeds it within a serialization context associated with a serializer factory by creating
* and instance of this class and registering that with a [SerializerFactory]
*
* Proxy serializers should transform an unserializable class into a representation that we can serialize
*
* @property serializer in instance of a user written serialization proxy, normally scanned and loaded
* automatically
* @property type the Java [Type] of the class which this serializes, inferred via reflection of the
* [serializer]'s super type
* @property proxyType the Java [Type] of the class into which instances of [type] are proxied for use byt
* the underlying serialisation engine
*
* @param factory a [SerializerFactory] belonging to the context this serializer is being instantiated
* for
*/
class CorDappCustomSerializer(
private val serialiser: SerializationCustomSerializer<*, *>,
factory: SerializerFactory)
: AMQPSerializer<Any>, SerializerFor {
private val serializer: SerializationCustomSerializer<*, *>,
factory: SerializerFactory) : AMQPSerializer<Any>, SerializerFor {
override val revealSubclassesInSchema: Boolean get() = false
override val type: Type get() = serialiser.type
private val types = serializer::class.supertypes.filter { it.jvmErasure == SerializationCustomSerializer::class }
.flatMap { it.arguments }
.map { it.type!!.javaType }
init {
if (types.size != 2) {
throw NotSerializableException("Unable to determine serializer parent types")
}
}
override val type = types[CORDAPP_TYPE]
val proxyType = types[PROXY_TYPE]
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(type)}")
val descriptor: Descriptor = Descriptor(typeDescriptor)
private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(serialiser.ptype, factory) }
private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyType, factory) }
override fun writeClassInfo(output: SerializationOutput) {}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
@Suppress("UNCHECKED_CAST")
val proxy = (serialiser as SerializationCustomSerializer<Any?,Any?>).toProxy(obj)
val proxy = uncheckedCast<SerializationCustomSerializer<*, *>,
SerializationCustomSerializer<Any?,Any?>> (serializer).toProxy(obj)
data.withDescribed(descriptor) {
data.withList {
@ -33,11 +79,10 @@ class CorDappCustomSerializer(
}
}
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput) =
@Suppress("UNCHECKED_CAST")
(serialiser as SerializationCustomSerializer<Any?,Any?>).fromProxy(
uncheckedCast(proxySerializer.readObject(obj, schema, input)))!!
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput) =
uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?,Any?>> (
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input)))!!
override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == type
override fun isSerializerFor(clazz: Class<*>) = clazz == type
}

View File

@ -204,8 +204,8 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
}
/**
* Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd
* if not use the [ClassCarpenter] to generate a class to use in it's place
* Iterate over an AMQP schema, for each type ascertain whether it's on ClassPath of [classloader] and,
* if not, use the [ClassCarpenter] to generate a class to use in it's place.
*/
private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) {
val metaSchema = CarpenterMetaSchema.newInstance()

View File

@ -2,27 +2,21 @@ package net.corda.nodeapi.internal.serialization.amqp
import org.junit.Test
import net.corda.core.serialization.CordaCustomSerializer
import net.corda.core.serialization.CordaCustomSerializerProxy
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationCustomSerializer
import org.assertj.core.api.Assertions
import java.io.NotSerializableException
import java.lang.reflect.Type
import kotlin.test.assertEquals
class CorDappSerializerTests {
data class NeedsProxy (val a: String)
@CordaCustomSerializer
class NeedsProxyProxySerializer : SerializationCustomSerializer {
@CordaCustomSerializerProxy
class NeedsProxyProxySerializer : SerializationCustomSerializer<NeedsProxy, NeedsProxyProxySerializer.Proxy> {
data class Proxy(val proxy_a_: String)
override val type: Type get() = NeedsProxy::class.java
override val ptype: Type get() = Proxy::class.java
override fun fromProxy(proxy: Any) : Any = NeedsProxy((proxy as Proxy).proxy_a_)
override fun toProxy(obj: Any) : Any = Proxy((obj as NeedsProxy).a)
override fun fromProxy(proxy: Proxy) = NeedsProxy(proxy.proxy_a_)
override fun toProxy(obj: NeedsProxy) = Proxy(obj.a)
}
// Standard proxy serializer used internally, here for comparison purposes
@ -34,12 +28,10 @@ class CorDappSerializerTests {
data class Proxy(val proxy_a_: String)
override fun toProxy(obj: NeedsProxy): Proxy {
println ("InternalProxySerializer - toProxy")
return Proxy(obj.a)
}
override fun fromProxy(proxy: Proxy): NeedsProxy {
println ("InternalProxySerializer - fromProxy")
return NeedsProxy(proxy.proxy_a_)
}
}