Review Comments

Refactor small bits of the serialiser, move things out of the read
methods and into the make factory companion object
This commit is contained in:
Katelyn Baker 2017-08-24 11:21:30 +01:00
parent 299b8026c7
commit cf9fbc715d
5 changed files with 43 additions and 31 deletions

View File

@ -6,6 +6,6 @@ package net.corda.core.serialization
*/
@Target(AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.RUNTIME)
annotation class CordaSerializerConstructor(val version: Int)
annotation class EvolvedSerializerConstructor(val version: Int)

View File

@ -1,6 +1,6 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.CordaSerializerConstructor
import net.corda.core.serialization.EvolvedSerializerConstructor
import net.corda.nodeapi.internal.serialization.carpenter.getTypeAsClass
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -16,7 +16,8 @@ import kotlin.reflect.jvm.javaType
class EvolutionSerializer(
clazz: Type,
factory: SerializerFactory,
val oldParams : Map<String, oldParam>,
val oldParams : Map<String, OldParam>,
val newParams : Map<String, Boolean>,
override val kotlinConstructor: KFunction<Any>?) : ObjectSerializer (clazz, factory) {
// explicitly null this out as we won't be using this list
@ -31,7 +32,10 @@ class EvolutionSerializer(
* order may have been changed and we need to know where into the list to look
* @param property object to read the actual property value
*/
data class oldParam (val type: Type, val idx: Int, val property: PropertySerializer)
data class OldParam (val type: Type, val idx: Int, val property: PropertySerializer) {
fun readProperty(paramValues: List<*>, schema: Schema, input: DeserializationInput) =
property.readProperty(paramValues[idx], schema, input)
}
companion object {
/**
@ -53,7 +57,7 @@ class EvolutionSerializer(
var maxConstructorVersion = Integer.MIN_VALUE
var constructor: KFunction<Any>? = null
clazz.kotlin.constructors.forEach {
val version = it.findAnnotation<CordaSerializerConstructor>()?.version ?: Integer.MIN_VALUE
val version = it.findAnnotation<EvolvedSerializerConstructor>()?.version ?: Integer.MIN_VALUE
if (oldArgumentSet.containsAll(it.parameters.map { v -> Pair(v.name, v.type.javaType) }) &&
version > maxConstructorVersion) {
constructor = it
@ -67,7 +71,7 @@ class EvolutionSerializer(
}
/**
* Build a serialization object for deserialisation only of objects serislaised
* Build a serialization object for deserialisation only of objects serialised
* as different versions of a class
*
* @param old is an object holding the schema that represents the object
@ -86,15 +90,17 @@ class EvolutionSerializer(
throw NotSerializableException(
"Attempt to deserialize an interface: new.type. Serialized form is invalid.")
val oldArgs = mutableMapOf<String, oldParam>()
val oldArgs = mutableMapOf<String, OldParam>()
var idx = 0
old.fields.forEach {
val returnType = it.getTypeAsClass(factory.classloader)
oldArgs[it.name] = oldParam(
oldArgs[it.name] = OldParam(
returnType, idx++, PropertySerializer.make(it.name, null, returnType, factory))
}
return EvolutionSerializer(new.type, factory, oldArgs, constructor)
val newArgs = constructor.parameters.associateBy({ it.name!! }, {it.type.isMarkedNullable})
return EvolutionSerializer(new.type, factory, oldArgs, newArgs, constructor)
}
}
@ -112,17 +118,14 @@ class EvolutionSerializer(
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any {
if (obj !is List<*>) throw NotSerializableException("Body of described type is unexpected $obj")
val newArgs = kotlinConstructor?.parameters?.associateBy({ it.name!! }, {it.type.isMarkedNullable}) ?:
throw NotSerializableException ("Bad Constructor selected for object $obj")
return construct(newArgs.map {
return construct(newParams.map {
val param = oldParams[it.key]
if (param == null && !it.value) {
throw NotSerializableException(
"New parameter ${it.key} is mandatory, should be nullable for evolution to worK")
}
param?.property?.readProperty(obj[param.idx], schema, input)
param?.readProperty(obj, schema, input)
})
}
}

View File

@ -15,6 +15,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
override val type: Type get() = clazz
open internal val propertySerializers: Collection<PropertySerializer>
open val kotlinConstructor = constructorForDeserialization(clazz)
val javaConstructor by lazy { kotlinConstructor?.javaConstructor }
init {
propertySerializers = propertiesForSerialization(kotlinConstructor, clazz, factory)
@ -71,9 +72,10 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
fun construct(properties: List<Any?>): Any {
val javaConstructor = kotlinConstructor?.javaConstructor ?:
if (javaConstructor == null) {
throw NotSerializableException("Attempt to deserialize an interface: $clazz. Serialized form is invalid.")
}
return javaConstructor.newInstance(*properties.toTypedArray())
return javaConstructor!!.newInstance(*properties.toTypedArray())
}
}
}

View File

@ -191,8 +191,8 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl : ClassLoader) {
val serialiser = processSchemaEntry(typeNotation)
// if we just successfully built a serialiser for the type but the type fingerprint
// doesn't match that of the serialised object then we are dealing with an older
// instance of the class, as such we need to build an evolverSerilaiser
// doesn't match that of the serialised object then we are dealing with different
// instance of the class, as such we need to build an EvolutionSerialiser
if (serialiser.typeDescriptor != typeNotation.descriptor.name) {
getEvolutionSerializer(typeNotation, serialiser as ObjectSerializer)
}

View File

@ -1,13 +1,20 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.CordaSerializerConstructor
import net.corda.core.serialization.EvolvedSerializerConstructor
import org.junit.Test
import java.io.File
import java.io.NotSerializableException
import kotlin.test.assertEquals
// To regenerate any of the binary test files do the following
//
// 1. Uncomment the code where the original form of the class is defined in the test
// 2. Comment out the rest of the test
// 3. Run the test
// 4. Using the printed path copy that file to the resources directory
// 5. Comment back out the generation code and uncomment the actual test
class EvolvabilityTests {
@Test
@ -104,7 +111,7 @@ class EvolvabilityTests {
// Expected to throw as we can't construct the new type as it contains a newly
// added parameter that isn't optional, i.e. not nullable and there isn't
// a compiler that takes the old parameters
// a constructor that takes the old parameters
DeserializationInput(sf).deserialize(SerializedBytes<C>(sc2))
}
@ -180,7 +187,7 @@ class EvolvabilityTests {
@Suppress("UNUSED")
data class CC (val a: Int, val b: String) {
@CordaSerializerConstructor(1)
@EvolvedSerializerConstructor(1)
constructor (a: Int) : this (a, "hello")
}
@ -239,7 +246,7 @@ class EvolvabilityTests {
data class CC (val a: Int, val b: Int, val c: String, val d: String) {
// ensure none of the original parameters align with the initial
// construction order
@CordaSerializerConstructor(1)
@EvolvedSerializerConstructor(1)
constructor (c: String, a: Int, b: Int) : this (a, b, c, "wibble")
}
@ -275,7 +282,7 @@ class EvolvabilityTests {
// ensure none of the original parameters align with the initial
// construction order
@Suppress("UNUSED")
@CordaSerializerConstructor(1)
@EvolvedSerializerConstructor(1)
constructor (c: String, a: Int) : this (a, c, "wibble")
}
@ -317,11 +324,11 @@ class EvolvabilityTests {
@Suppress("UNUSED")
data class C (val e: Int, val c: Int, val b: Int, val a: Int, val d: Int) {
@CordaSerializerConstructor(1)
@EvolvedSerializerConstructor(1)
constructor (b: Int, a: Int) : this (-1, -1, b, a, -1)
@CordaSerializerConstructor(2)
@EvolvedSerializerConstructor(2)
constructor (a: Int, c: Int, b: Int) : this (-1, c, b, a, -1)
@CordaSerializerConstructor(3)
@EvolvedSerializerConstructor(3)
constructor (a: Int, b: Int, c: Int, d: Int) : this (-1, c, b, a, d)
}
@ -411,13 +418,13 @@ class EvolvabilityTests {
@Suppress("UNUSED")
data class C (val b: Int, val c: Int, val d: Int, val e: Int, val f: Int, val g: Int) {
@CordaSerializerConstructor(1)
@EvolvedSerializerConstructor(1)
constructor (b: Int, c: Int) : this (b, c, -1, -1, -1, -1)
@CordaSerializerConstructor(2)
@EvolvedSerializerConstructor(2)
constructor (b: Int, c: Int, d: Int) : this (b, c, d, -1, -1, -1)
@CordaSerializerConstructor(3)
@EvolvedSerializerConstructor(3)
constructor (b: Int, c: Int, d: Int, e: Int) : this (b, c, d, e, -1, -1)
@CordaSerializerConstructor(4)
@EvolvedSerializerConstructor(4)
constructor (b: Int, c: Int, d: Int, e: Int, f: Int) : this (b, c, d, e, f, -1)
}