diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index 9dafe348d4..1eba19c13f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -219,8 +219,8 @@ class SerializerFactory(val whitelist: ClassWhitelist = AllWhitelist) { } } - private fun makeClassSerializer(clazz: Class<*>, type: Type, declaredType: Type): AMQPSerializer { - return serializersByType.computeIfAbsent(type) { + private fun makeClassSerializer(clazz: Class<*>, type: Type, declaredType: Type): AMQPSerializer = + serializersByType.computeIfAbsent(type) { if (isPrimitive(clazz)) { AMQPPrimitiveSerializer(clazz) } else { @@ -238,7 +238,6 @@ class SerializerFactory(val whitelist: ClassWhitelist = AllWhitelist) { } } } - } } internal fun findCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer? { @@ -319,10 +318,10 @@ class SerializerFactory(val whitelist: ClassWhitelist = AllWhitelist) { fun nameForType(type: Type) : String = when (type) { is Class<*> -> { primitiveTypeName(type) ?: if (type.isArray) { - "${nameForType(type.componentType)}${if(type.componentType.isPrimitive)"[p]" else "[]"}" + "${nameForType(type.componentType)}${if (type.componentType.isPrimitive) "[p]" else "[]"}" } else type.name } - is ParameterizedType -> "${nameForType(type.rawType)}<${type.actualTypeArguments.joinToString { nameForType(it) }}>" + is ParameterizedType -> "${nameForType(type.rawType)ype.actualTypeArguments.joinToString { nameForType(it) }}>" is GenericArrayType -> "${nameForType(type.genericComponentType)}[]" else -> throw NotSerializableException("Unable to render type $type to a string.") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt index ad7e8d4ca6..21641a0ae1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt @@ -129,7 +129,7 @@ class ClassCarpenter { private fun generateClass(classSchema: Schema): Class<*> { return generate(classSchema) { cw, schema -> val superName = schema.superclass?.jvmName ?: "java/lang/Object" - var interfaces = schema.interfaces.map { it.name.jvm }.toMutableList() + val interfaces = schema.interfaces.map { it.name.jvm }.toMutableList() if (SimpleFieldAccess::class.java !in schema.interfaces) interfaces.add(SimpleFieldAccess::class.java.name.jvm) @@ -309,7 +309,6 @@ class ClassCarpenter { // If we're trying to carpent a class that prior to serialisation / deserialisation // was made by a carpenter then we can ignore this (it will implement a plain get // method from SimpleFieldAccess). - // method from SimpleFieldAccess) if (fieldNameFromItf.isEmpty() && SimpleFieldAccess::class.java in schema.interfaces) return@forEach if ((schema is ClassSchema) and (fieldNameFromItf !in allFields)) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt index ea74d2ad82..096d2fa7a0 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt @@ -10,8 +10,21 @@ interface I { } interface II { + fun returnName() : String +} + +interface III { + fun returnAge() : Int + fun returnThingWithName(): II +} + +interface B { + fun getName() : String +} + +interface BB { fun getAge() : Int - fun getThingWithName(): I + fun getThingWithName(): II } /** @@ -21,7 +34,6 @@ interface II { * replicates the situation where a receiver doesn't have some or all elements of a schema present on it's classpath */ class DeserializeNeedingCarpentryTests { - companion object { /** * If you want to see the schema encoded into the envelope after serialisation change this to true @@ -116,37 +128,85 @@ class DeserializeNeedingCarpentryTests { } } + // technically this test doesn't test anything (relevent to carpanter / serialiser interaction) since + // all the classes are knwon, what does do is replicate the test below to demonstrate it should + // all work + @Test + fun mapOfKnown() { + class lII (val name: String) : II { + override fun returnName() = name + } + + class lIII (val age: Int, val thingWithName: II): III { + override fun returnAge(): Int = age + override fun returnThingWithName() = thingWithName + } + + data class Wrapper(val IIIs: MutableMap) + val wrapper = Wrapper (mutableMapOf()) + val testData = arrayOf(Pair ("Fred", 12), Pair ("Bob", 50), Pair ("Thirsty", 101)) + + testData.forEach { + wrapper.IIIs[it.first] = lIII(it.second, lII(it.first)) + } + + // Now do the actual test by serialising and deserialising [wrapper] + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(wrapper) + val deserializedObj = DeserializationInput(sf).deserialize(serialisedBytes) + } + + // TODO This class shows that the problem isn't with the carpented class but a general + // TODO Bug / feature of the code... + /* + @Test + fun linkedHashMapTest() { + data class C(val c : LinkedHashMap) + val c = C (LinkedHashMap (mapOf("A" to 1, "B" to 2))) + + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) + val deserializedObj = DeserializationInput(sf).deserialize(serialisedBytes) + + } + */ + + // TODO the problem here is that the wrapper class as created by the serialiser + // TODO contains a [LinkedHashMap] and not a [Map] and we thus can't serialise + // TODO it - Talk to Rick about weather we should be able to or not + /* @Test fun mapOfInterfaces() { val cc = ClassCarpenter() val implementsI = cc.build(ClassSchema( "implementsI", mapOf("name" to NonNullableField(String::class.java)), - interfaces = listOf (I::class.java))) + interfaces = listOf (B::class.java))) val implementsII = cc.build(ClassSchema("ImplementsII", mapOf ( "age" to NonNullableField(Int::class.java), - "thingWithName" to NullableField(I::class.java)), - interfaces = listOf (II::class.java))) + "thingWithName" to NullableField(B::class.java)), + interfaces = listOf (BB::class.java))) + + // inline fun getval(reified T : Any) : return T::class.java val wrapper = cc.build(ClassSchema("wrapper", mapOf ( - "IIs" to NonNullableField(MutableMap::class.java)))) + "BBs" to NonNullableField(mutableMapOf()::class.java + )))) - val tmp: MutableMap = mutableMapOf() + val tmp : MutableMap = mutableMapOf() val toSerialise = wrapper.constructors.first().newInstance(tmp) val testData = arrayOf(Pair ("Fred", 12), Pair ("Bob", 50), Pair ("Thirsty", 101)) testData.forEach { - (wrapper.getMethod("getIIs").invoke(toSerialise) as MutableMap)[it.first] = + (wrapper.getMethod("getBBs").invoke(toSerialise) as MutableMap)[it.first] = implementsII.constructors.first().newInstance(it.second, - implementsI.constructors.first().newInstance(it.first) as I) as II + implementsI.constructors.first().newInstance(it.first) as B) as BB } // Now do the actual test by serialising and deserialising [wrapper] - val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(wrapper) + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(toSerialise) val deserializedObj = DeserializationInput(sf).deserialize(serialisedBytes) - } + */ @Test fun unknownInterface() {