mirror of
https://github.com/corda/corda.git
synced 2025-06-14 05:08:18 +00:00
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:
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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_)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user