CORDA-904 - Fix evolver to work with setter instantiated classses (#2463)

- Cherry pick to backport from master

* CORDA-904 - Make evolver work with classes that use setters

* review comments

* review comments

* small fixs

* don't include systemTest in compiler.xml
This commit is contained in:
Katelyn Baker 2018-02-06 12:55:49 +00:00
parent f8359a74fd
commit f16e45abe9
3 changed files with 19 additions and 13 deletions

2
.idea/compiler.xml generated
View File

@ -159,4 +159,4 @@
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
</component>
</project>
</project>

View File

@ -60,6 +60,7 @@ abstract class EvolutionSerializer(
*/
private fun getEvolverConstructor(type: Type, oldArgs: Map<String, OldParam>): KFunction<Any>? {
val clazz: Class<*> = type.asClass()!!
if (!isConcrete(clazz)) return null
val oldArgumentSet = oldArgs.map { Pair(it.key as String?, it.value.property.resolvedType) }
@ -123,7 +124,6 @@ abstract class EvolutionSerializer(
*/
fun make(old: CompositeType, new: ObjectSerializer,
factory: SerializerFactory): AMQPSerializer<Any> {
// The order in which the properties were serialised is important and must be preserved
val readersAsSerialized = LinkedHashMap<String, OldParam>()
old.fields.forEach {
@ -135,9 +135,9 @@ abstract class EvolutionSerializer(
}
}
val constructor = getEvolverConstructor(new.type, readersAsSerialized) ?:
throw NotSerializableException(
"Attempt to deserialize an interface: ${new.type}. Serialized form is invalid.")
// cope with the situation where a generic interface was serialised as a type, in such cases
// return the synthesised object which is, given the absence of a constructor, a no op
val constructor = getEvolverConstructor(new.type, readersAsSerialized) ?: return new
val classProperties = new.type.asClass()?.propertyDescriptors() ?: emptyMap()

View File

@ -98,7 +98,6 @@ data class PropertyDescriptor(var field: Field?, var setter: Method?, var getter
}.toString()
constructor() : this(null, null, null, null)
constructor(field: Field?) : this(field, null, null, null)
fun preferredGetter() : Method? = getter ?: iser
}
@ -128,9 +127,16 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
val classProperties = mutableMapOf<String, PropertyDescriptor>()
var clazz: Class<out Any?>? = this
clazz!!.declaredFields.forEach { classProperties.put(it.name, PropertyDescriptor(it)) }
do {
clazz!!.declaredFields.forEach { property ->
classProperties.computeIfAbsent(property.name) {
PropertyDescriptor()
}.apply {
this.field = property
}
}
// Note: It is possible for a class to have multiple instances of a function where the types
// differ. For example:
// interface I<out T> { val a: T }
@ -142,7 +148,7 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
//
// In addition, only getters that take zero parameters and setters that take a single
// parameter will be considered
clazz!!.declaredMethods?.map { func ->
clazz.declaredMethods?.map { func ->
if (!Modifier.isPublic(func.modifiers)) return@map
if (func.name == "getClass") return@map
@ -231,15 +237,13 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
Pair(PublicPropertyReader(getter), returnType)
} else {
try {
val field = clazz.getDeclaredField(param.value.name)
Pair(PrivatePropertyReader(field, type), field.genericType)
} catch (e: NoSuchFieldException) {
val field = classProperties[name]!!.field ?:
throw NotSerializableException("No property matching constructor parameter named '$name' " +
"of '$clazz'. If using Java, check that you have the -parameters option specified " +
"in the Java compiler. Alternately, provide a proxy serializer " +
"(SerializationCustomSerializer) if recompiling isn't an option")
}
Pair(PrivatePropertyReader(field, type), field.genericType)
}
} else {
throw NotSerializableException(
@ -324,6 +328,8 @@ private fun propertiesForSerializationFromAbstract(
return mutableListOf<PropertyAccessorConstructor>().apply {
properties.toList().withIndex().forEach {
val getter = it.value.second.getter ?: return@forEach
if (it.value.second.field == null) return@forEach
val returnType = resolveTypeVariables(getter.genericReturnType, type)
this += PropertyAccessorConstructor(
it.index,