mirror of
https://github.com/corda/corda.git
synced 2025-04-14 14:37:22 +00:00
Add Evolvability to Serializer
If we attempt to deserialize a class and find that since it's serialization the definition has changed we need to create a serializer capable of evolving the serialised data and constructing an instance of the new type We currently cope with * Removing members * Adding nullable members * Adding non nullable members if a constructor is provided that allows us to set the old arguments and defaults the new (mandatory) fields * Reordering paramters
This commit is contained in:
parent
db07d717e0
commit
1d131eced5
node-api/src
main/kotlin/net/corda/nodeapi/internal/serialization/amqp
test
kotlin/net/corda/nodeapi/internal/serialization/amqp
resources/net/corda/nodeapi/internal/serialization/amqp
@ -17,6 +17,7 @@ class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPSerial
|
||||
internal val propertySerializers: Collection<PropertySerializer>
|
||||
|
||||
init {
|
||||
println ("Object Serializer")
|
||||
val kotlinConstructor = constructorForDeserialization(clazz)
|
||||
javaConstructor = kotlinConstructor?.javaConstructor
|
||||
javaConstructor?.isAccessible = true
|
||||
|
@ -68,6 +68,7 @@ internal fun <T : Any> propertiesForSerialization(kotlinConstructor: KFunction<T
|
||||
private fun isConcrete(clazz: Class<*>): Boolean = !(clazz.isInterface || Modifier.isAbstract(clazz.modifiers))
|
||||
|
||||
private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructor: KFunction<T>, type: Type, factory: SerializerFactory): Collection<PropertySerializer> {
|
||||
println ("propertiesForSerializationFromConstructor - $type")
|
||||
val clazz = (kotlinConstructor.returnType.classifier as KClass<*>).javaObjectType
|
||||
// Kotlin reflection doesn't work with Java getters the way you might expect, so we drop back to good ol' beans.
|
||||
val properties = Introspector.getBeanInfo(clazz).propertyDescriptors.filter { it.name != "class" }.groupBy { it.name }.mapValues { it.value[0] }
|
||||
|
@ -56,11 +56,14 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl : ClassLoader) {
|
||||
*/
|
||||
@Throws(NotSerializableException::class)
|
||||
fun get(actualClass: Class<*>?, declaredType: Type): AMQPSerializer<Any> {
|
||||
println ("get - $declaredType")
|
||||
val declaredClass = declaredType.asClass() ?: throw NotSerializableException(
|
||||
"Declared types of $declaredType are not supported.")
|
||||
|
||||
val actualType: Type = inferTypeVariables(actualClass, declaredClass, declaredType) ?: declaredType
|
||||
|
||||
println ("actual type - $actualType")
|
||||
|
||||
val serializer = if (Collection::class.java.isAssignableFrom(declaredClass)) {
|
||||
serializersByType.computeIfAbsent(declaredType) {
|
||||
CollectionSerializer(declaredType as? ParameterizedType ?: DeserializedParameterizedType(
|
||||
@ -182,9 +185,11 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl : ClassLoader) {
|
||||
private fun processSchema(schema: Schema, sentinel: Boolean = false) {
|
||||
val carpenterSchemas = CarpenterSchemas.newInstance()
|
||||
for (typeNotation in schema.types) {
|
||||
println("processSchema: ${typeNotation.descriptor} ${typeNotation.name}")
|
||||
try {
|
||||
processSchemaEntry(typeNotation)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
println("poop")
|
||||
if (sentinel || (typeNotation !is CompositeType)) throw e
|
||||
typeNotation.carpenterSchema(classloader, carpenterSchemas = carpenterSchemas)
|
||||
}
|
||||
@ -212,6 +217,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl : ClassLoader) {
|
||||
|
||||
private fun processCompositeType(typeNotation: CompositeType) {
|
||||
// TODO: class loader logic, and compare the schema.
|
||||
println("processCompositeType")
|
||||
val type = typeForName(typeNotation.name, classloader)
|
||||
get(type.asClass() ?: throw NotSerializableException("Unable to build composite type for $type"), type)
|
||||
}
|
||||
|
37
node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt
Normal file
37
node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt
Normal file
@ -0,0 +1,37 @@
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.utilities.toHexString
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
|
||||
class EvolvabilityTests {
|
||||
|
||||
@Test
|
||||
fun test1() {
|
||||
val sf = SerializerFactory()
|
||||
|
||||
// Basis for the serialised version
|
||||
// data class C (val a: Int)
|
||||
// var sc = SerializationOutput(sf).serialize(C(1))
|
||||
|
||||
data class C (val a: Int, val b: Int)
|
||||
|
||||
val path = EvolvabilityTests::class.java.getResource("EvolvabilityTests.test1")
|
||||
println ("PATH = $path")
|
||||
val f = File(path.toURI())
|
||||
|
||||
println (sf)
|
||||
// var sc = SerializationOutput(sf).serialize(C(1))
|
||||
// f.writeBytes(sc.bytes)
|
||||
val sc2 = f.readBytes()
|
||||
|
||||
var deserializedC = DeserializationInput().deserialize(SerializedBytes<C>(sc2))
|
||||
|
||||
println (deserializedC.a)
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
BIN
node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.test1
Normal file
BIN
node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.test1
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user