Add recursive dependency resolution to schema generation

A complete amqp schema can now be used to generate a set of carpetner
schemas representing all of the classes not found on the deserialzing
end.

A dependency and depdendent chain is setup such that all classes are
created in the order required

Squashed commit messages:
	* IntelliJ reformat of the code

	* Merge the interface synthesis changes and rebase onto the tip of master

	  Somethign is very broken in the AMQP -> Carpenter schema code but want a
	  commit so I at least know the actual carpenter is merged

	* Nested schema creation now works with dependencies recursvly created in
	  the carpenter

	* Unit test fixes

	* Remove spurious prints from tests

	* Remove warnings

	* Don't add cladd member dep by name

	  Since that's the name of the field, not the type we depend on. If we do
	  we'll never actually be able to craft the type as the dependency chain
	  will be horribly broken

	  Various bug fixes
This commit is contained in:
Katelyn Baker 2017-06-23 09:02:00 +01:00
parent ce172887d0
commit b4a62722fc
11 changed files with 855 additions and 263 deletions

View File

@ -15,7 +15,9 @@ import java.lang.reflect.Type
import java.lang.reflect.TypeVariable
import java.util.*
import net.corda.core.serialization.ClassCarpenterSchema
import net.corda.core.serialization.carpenter.CarpenterSchemas
import net.corda.core.serialization.carpenter.Schema as CarpenterSchema
import net.corda.core.serialization.carpenter.CarpenterSchemaFactory
// TODO: get an assigned number as per AMQP spec
val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000
@ -25,6 +27,24 @@ val DESCRIPTOR_DOMAIN: String = "net.corda"
// "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB
val AmqpHeaderV1_0: OpaqueBytes = OpaqueBytes("corda\u0001\u0000\u0000".toByteArray())
private fun List<ClassLoader>.exists (clazz: String) =
this.find { try { it.loadClass(clazz); true } catch (e: ClassNotFoundException) { false } } != null
private fun List<ClassLoader>.loadIfExists (clazz: String) : Class<*> {
this.forEach {
try {
return it.loadClass(clazz)
} catch (e: ClassNotFoundException) {
return@forEach
}
}
throw ClassNotFoundException(clazz)
}
class UncarpentableException (name: String, field: String, type: String) :
Throwable ("Class $name is loadable yet contains field $field of unknown type $type")
/**
* This class wraps all serialized data, so that the schema can be carried along with it. We will provide various internal utilities
* to decompose and recompose with/without schema etc so that e.g. we can store objects with a (relationally) normalised out schema to
@ -91,28 +111,12 @@ data class Schema(val types: List<TypeNotation>) : DescribedType {
override fun toString(): String = types.joinToString("\n")
fun carpenterSchema(loaders : List<ClassLoader> = listOf<ClassLoader>(ClassLoader.getSystemClassLoader()))
: List<ClassCarpenterSchema>
: CarpenterSchemas
{
var rtn = mutableListOf<ClassCarpenterSchema>()
var rtn = CarpenterSchemas.newInstance()
for (type in types) {
if (type is CompositeType) {
var foundIt = false
for (loader in loaders) {
try {
loader.loadClass(type.name)
foundIt = true
break
} catch (e: ClassNotFoundException) {
continue
}
}
if (foundIt) continue
else {
rtn.add(type.carpenterSchema())
}
}
types.filterIsInstance<CompositeType>().forEach {
it.carpenterSchema(classLoaders = loaders, carpenterSchemas = rtn)
}
return rtn
@ -199,17 +203,9 @@ data class Field(var name: String, val type: String, val requires: List<String>,
return sb.toString()
}
inline fun isKnownClass (type : String) : Class<*> {
try {
return ClassLoader.getSystemClassLoader().loadClass(type)
}
catch (e: ClassNotFoundException) {
// call carpenter
throw IllegalArgumentException ("${type} - ${name} - pants")
}
}
fun getPrimType() = when (type) {
fun getTypeAsClass(
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())
) = when (type) {
"int" -> Int::class.javaPrimitiveType!!
"string" -> String::class.java
"short" -> Short::class.javaPrimitiveType!!
@ -217,10 +213,27 @@ data class Field(var name: String, val type: String, val requires: List<String>,
"char" -> Char::class.javaPrimitiveType!!
"boolean" -> Boolean::class.javaPrimitiveType!!
"double" -> Double::class.javaPrimitiveType!!
"float" -> Double::class.javaPrimitiveType!!
"*" -> isKnownClass(requires[0])
else -> isKnownClass(type)
"float" -> Float::class.javaPrimitiveType!!
"*" -> classLoaders.loadIfExists(requires[0])
else -> classLoaders.loadIfExists(type)
}
fun validateType(
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())
) = when (type) {
"int" -> Int::class.javaPrimitiveType!!
"string" -> String::class.java
"short" -> Short::class.javaPrimitiveType!!
"long" -> Long::class.javaPrimitiveType!!
"char" -> Char::class.javaPrimitiveType!!
"boolean" -> Boolean::class.javaPrimitiveType!!
"double" -> Double::class.javaPrimitiveType!!
"float" -> Float::class.javaPrimitiveType!!
"*" -> if (classLoaders.exists(requires[0])) requires[0] else null
else -> if (classLoaders.exists (type)) type else null
}
fun typeAsString() = if (type =="*") requires[0] else type
}
sealed class TypeNotation : DescribedType {
@ -287,32 +300,69 @@ data class CompositeType(override var name: String, override val label: String?,
return sb.toString()
}
fun carpenterSchema(classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())) : ClassCarpenterSchema {
var m : MutableMap<String, Class<out Any?>> = mutableMapOf()
/** if we can load the class then we MUST know about all of it's sub elements,
otherwise we couldn't know about this */
private fun validateKnown (
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader()))
{
fields.forEach {
if (it.validateType(classLoaders) == null) throw UncarpentableException (name, it.name, it.type)
}
}
fields.forEach { m[it.name] = it.getPrimType() }
fun carpenterSchema(
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader()),
carpenterSchemas : CarpenterSchemas,
force : Boolean = false)
{
/* first question, do we know about this type or not */
if (classLoaders.exists(name)) {
validateKnown(classLoaders)
var providesList = mutableListOf<Class<*>>()
for (iface in provides) {
var found = false
for (loader in classLoaders) {
try {
providesList.add (loader.loadClass(iface))
found = true
break
}
catch (e: ClassNotFoundException) {
continue
}
}
if (found == false) {
println ("This needs to work but it wont - ${iface}")
}
else println ("found it ${iface}")
if (!force) return
}
return ClassCarpenterSchema (name, m, interfaces = providesList)
val providesList = mutableListOf<Class<*>>()
var isInterface = false
var isCreatable = true
provides.forEach {
if (name.equals(it)) {
isInterface = true
return@forEach
}
try {
providesList.add (classLoaders.loadIfExists(it))
}
catch (e: ClassNotFoundException) {
carpenterSchemas.addDepPair(this, name, it)
isCreatable = false
}
}
val m : MutableMap<String, Class<out Any?>> = mutableMapOf()
fields.forEach {
try {
m[it.name] = it.getTypeAsClass(classLoaders)
}
catch (e: ClassNotFoundException) {
carpenterSchemas.addDepPair(this, name, it.typeAsString())
isCreatable = false
}
}
if (isCreatable) {
carpenterSchemas.carpenterSchemas.add (CarpenterSchemaFactory.newInstance(
name = name,
fields = m,
interfaces = providesList,
isInterface = isInterface))
}
}
}

View File

@ -8,6 +8,10 @@ import org.objectweb.asm.Type
import java.lang.Character.isJavaIdentifierPart
import java.lang.Character.isJavaIdentifierStart
import net.corda.core.serialization.carpenter.Schema
import net.corda.core.serialization.carpenter.ClassSchema
import net.corda.core.serialization.carpenter.InterfaceSchema
import java.util.*
/**
@ -64,6 +68,15 @@ interface SimpleFieldAccess {
*
* Equals/hashCode methods are not yet supported.
*/
/**********************************************************************************************************************/
class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
}
/**********************************************************************************************************************/
class ClassCarpenter {
// TODO: Generics.
// TODO: Sandbox the generated code when a security manager is in use.
@ -159,18 +172,13 @@ class ClassCarpenter {
}
}
private class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
}
private val classloader = CarpenterClassLoader()
fun classLoader() = classloader as ClassLoader
val classloader = CarpenterClassLoader()
private val _loaded = HashMap<String, Class<*>>()
private val String.jvm: String get() = replace(".", "/")
/** Returns a snapshot of the currently loaded classes as a map of full class name (package names+dots) -> class object */
fun loaded() : Map<String, Class<*>> = HashMap(_loaded)
val loaded: Map<String, Class<*>> = HashMap(_loaded)
/**
* Generate bytecode for the given schema and load into the JVM. The returned class object can be used to
@ -178,17 +186,18 @@ class ClassCarpenter {
*
* @throws DuplicateNameException if the schema's name is already taken in this namespace (you can create a new ClassCarpenter if you're OK with ambiguous names)
*/
fun build(schema: ClassCarpenterSchema): Class<*> {
fun build(schema: Schema): Class<*> {
validateSchema(schema)
// Walk up the inheritance hierarchy and then start walking back down once we either hit the top, or
// find a class we haven't generated yet.
val hierarchy = ArrayList<ClassCarpenterSchema>()
val hierarchy = ArrayList<Schema>()
hierarchy += schema
var cursor = schema.superclass
while (cursor != null && cursor.name !in _loaded) {
hierarchy += cursor
cursor = cursor.superclass
}
hierarchy.reversed().forEach {
when (it) {
is InterfaceSchema -> generateInterface(it)
@ -196,6 +205,8 @@ class ClassCarpenter {
}
}
assert (schema.name in _loaded)
return _loaded[schema.name]!!
}
@ -288,7 +299,8 @@ class ClassCarpenter {
private fun ClassWriter.generateGetters(schema: Schema) {
for ((name, type) in schema.fields) {
with(visitMethod(ACC_PUBLIC, "get" + name.capitalize(), "()" + type.descriptor, null, null)) {
val opcodes = ACC_PUBLIC
with(visitMethod(opcodes, "get" + name.capitalize(), "()" + type.descriptor, null, null)) {
type.addNullabilityAnnotation(this)
visitCode()
visitVarInsn(ALOAD, 0) // Load 'this'
@ -305,7 +317,6 @@ class ClassCarpenter {
visitEnd()
}
}
}
private fun ClassWriter.generateAbstractGetters(schema: Schema) {

View File

@ -0,0 +1,28 @@
package net.corda.core.serialization.carpenter
import net.corda.core.serialization.amqp.TypeNotation
data class CarpenterSchemas (
val carpenterSchemas : MutableList<Schema>,
val dependencies : MutableMap<String, Pair<TypeNotation, MutableList<String>>>,
val dependsOn : MutableMap<String, MutableList<String>>) {
companion object CarpenterSchemaConstructor {
fun newInstance(): CarpenterSchemas {
return CarpenterSchemas(
mutableListOf<Schema>(),
mutableMapOf<String, Pair<TypeNotation, MutableList<String>>>(),
mutableMapOf<String, MutableList<String>>())
}
}
fun addDepPair(type: TypeNotation, dependant: String, dependee: String) {
fun String.name() = this.split ('.').last().split('$').last()
println ("add dep ${dependant.name()} on ${dependee.name()}")
dependsOn.computeIfAbsent(dependee, { mutableListOf<String>() }).add(dependant)
dependencies.computeIfAbsent(dependant, { Pair(type, mutableListOf<String>()) }).second.add(dependee)
}
val size
get() = carpenterSchemas.size
}

View File

@ -34,11 +34,23 @@ class ClassSchema(
fields: Map<String, Class<out Any?>>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : ClassCarpenter.Schema (name, fields, superclass, interfaces)
) : Schema (name, fields, superclass, interfaces)
class InterfaceSchema(
name: String,
fields: Map<String, Class<out Any?>>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : ClassCarpenter.Schema (name, fields, superclass, interfaces)
) : Schema (name, fields, superclass, interfaces)
object CarpenterSchemaFactory {
fun newInstance (
name: String,
fields: Map<String, Class<out Any?>>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList(),
isInterface: Boolean = false
) : Schema =
if (isInterface) InterfaceSchema (name, fields, superclass, interfaces)
else ClassSchema (name, fields, superclass, interfaces)
}

View File

@ -8,31 +8,11 @@ import java.beans.Introspector
import kotlin.test.assertNotEquals
class ClassCarpenterTest {
/*
cw.visitInnerClass(
"net/corda/carpenter/ClassCarpenterTest$DummyInterface",
"net/corda/carpenter/ClassCarpenterTest",
"DummyInterface",
ACC_PUBLIC + ACC_STATIC + ACC_ABSTRACT + ACC_INTERFACE);
*/
interface DummyInterface {
val a: String
val b: Int
}
/*
cw.visitInnerClass(
"net/corda/carpenter/ClassCarpenterTest$Dummy",
"net/corda/carpenter/ClassCarpenterTest",
"Dummy",
ACC_PUBLIC + ACC_FINAL + ACC_STATIC);
*/
class Dummy (override val a: String, override val b: Int) : DummyInterface
val dummy = Dummy ("hi", 1)
val dummy2 = Dummy2 ("hi", 1)
val cc = ClassCarpenter()
// We have to ignore synthetic fields even though ClassCarpenter doesn't create any because the JaCoCo
@ -191,7 +171,7 @@ class ClassCarpenterTest {
assertEquals(iface.declaredMethods.size, 1)
assertEquals(iface.declaredMethods[0].name, "getA")
val schema2 = ClassCarpenter.ClassSchema(
val schema2 = ClassSchema(
"gen.Derived",
mapOf("a" to ClassCarpenter.NonNullableField (Int::class.java)),
interfaces = listOf(iface))
@ -217,7 +197,7 @@ class ClassCarpenterTest {
"c" to ClassCarpenter.NonNullableField(Int::class.java),
"d" to ClassCarpenter.NonNullableField(String::class.java)))
val class1 = ClassCarpenter.ClassSchema(
val class1 = ClassSchema(
"gen.Derived",
mapOf(
"a" to ClassCarpenter.NonNullableField(Int::class.java),
@ -241,20 +221,20 @@ class ClassCarpenterTest {
@Test
fun `interface implementing interface`() {
val iFace1 = ClassCarpenter.InterfaceSchema(
val iFace1 = InterfaceSchema(
"gen.Interface1",
mapOf(
"a" to ClassCarpenter.NonNullableField (Int::class.java),
"b" to ClassCarpenter.NonNullableField(String::class.java)))
val iFace2 = ClassCarpenter.InterfaceSchema(
val iFace2 = InterfaceSchema(
"gen.Interface2",
mapOf(
"c" to ClassCarpenter.NonNullableField(Int::class.java),
"d" to ClassCarpenter.NonNullableField(String::class.java)),
interfaces = listOf(cc.build(iFace1)))
val class1 = ClassCarpenter.ClassSchema(
val class1 = ClassSchema(
"gen.Derived",
mapOf(
"a" to ClassCarpenter.NonNullableField(Int::class.java),

View File

@ -0,0 +1,53 @@
package net.corda.carpenter
import net.corda.core.serialization.carpenter.CarpenterSchemas
import net.corda.core.serialization.carpenter.Schema
import net.corda.core.serialization.amqp.CompositeType
abstract class MetaCarpenterBase (val schemas : CarpenterSchemas) {
private val cc = ClassCarpenter()
val objects = mutableMapOf<String, Class<*>>()
fun step (newObject : Schema) {
objects[newObject.name] = cc.build (newObject)
/* go over the list of everything that had a dependency on the newly
carpented class existing and remove it from their dependency list, If that
list is now empty we have no impediment to carpenting that class up */
schemas.dependsOn.remove(newObject.name)?.forEach { dependent ->
assert (newObject.name in schemas.dependencies[dependent]!!.second)
schemas.dependencies[dependent]?.second?.remove(newObject.name)
/* we're out of blockers so we can now create the type */
if (schemas.dependencies[dependent]?.second?.isEmpty() ?: false) {
(schemas.dependencies.remove (dependent)?.first as CompositeType).carpenterSchema (
classLoaders = listOf<ClassLoader> (
ClassLoader.getSystemClassLoader(),
cc.classloader),
carpenterSchemas = schemas)
}
}
}
abstract fun build()
}
class MetaCarpenter (schemas : CarpenterSchemas) : MetaCarpenterBase (schemas) {
override fun build() {
while (schemas.carpenterSchemas.isNotEmpty()) {
val newObject = schemas.carpenterSchemas.removeAt(0)
step (newObject)
}
}
}
class TestMetaCarpenter (schemas : CarpenterSchemas) : MetaCarpenterBase (schemas) {
override fun build() {
println ("TestMetaCarpenter::build")
if (schemas.carpenterSchemas.isEmpty()) return
step (schemas.carpenterSchemas.removeAt(0))
}
}

View File

@ -0,0 +1,56 @@
package net.corda.carpenter.test
import net.corda.core.serialization.amqp.CompositeType
import net.corda.core.serialization.amqp.Field
import net.corda.core.serialization.amqp.Schema
import net.corda.core.serialization.amqp.TypeNotation
import net.corda.core.serialization.amqp.SerializerFactory
import net.corda.core.serialization.amqp.SerializationOutput
/**********************************************************************************************************************/
fun curruptName(name: String) = "${name}__carpenter"
/**********************************************************************************************************************/
/* given a list of class names work through the amqp envelope schema and alter any that
match in the fashion defined above */
fun Schema.curruptName (names: List<String>) : Schema {
val newTypes : MutableList<TypeNotation> = mutableListOf()
for (type in types) {
val newName = if (type.name in names) curruptName (type.name) else type.name
val newProvides = type.provides.map {
it -> if (it in names) curruptName (it) else it
}
val newFields = mutableListOf<Field>()
(type as CompositeType).fields.forEach {
val type = if (it.type in names) curruptName (it.type) else it.type
val requires = if (it.requires.isNotEmpty() && (it.requires[0] in names))
listOf (curruptName (it.requires[0])) else it.requires
newFields.add (it.copy (type=type, requires=requires))
}
newTypes.add (type.copy (name=newName, provides=newProvides, fields=newFields))
}
return Schema (types=newTypes)
}
/**********************************************************************************************************************/
open class AmqpCarpenterBase {
var factory = SerializerFactory()
fun serialise (clazz : Any) = SerializationOutput(factory).serialize(clazz)
fun testName() = Thread.currentThread().stackTrace[2].methodName
inline fun classTestName(clazz: String) = "${this.javaClass.name}\$${testName()}\$$clazz"
}
/**********************************************************************************************************************/

View File

@ -2,17 +2,25 @@ package net.corda.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.amqp.*
import net.corda.core.serialization.ClassCarpenterSchema
import org.junit.Test
import kotlin.test.assertEquals
import net.corda.carpenter.test.*
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class Inheritance {
private var factory = SerializerFactory()
@CordaSerializable
interface I_ {
val a: Int
}
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
/*
* Where a class has a member that is also a composite type or interface
*/
class CompositeMembers : AmqpCarpenterBase() {
@Test
fun nestedInts() {
fun bothKnown () {
val testA = 10
val testB = 20
@ -20,9 +28,9 @@ class Inheritance {
data class A(val a: Int)
@CordaSerializable
class B (val a: A, var b: Int)
data class B (val a: A, var b: Int)
var b = B(A(testA), testB)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
@ -36,9 +44,6 @@ class Inheritance {
assert(obj.second.schema.types[0] is CompositeType)
assert(obj.second.schema.types[1] is CompositeType)
println (obj.second.schema.types[0] as CompositeType)
println (obj.second.schema.types[1] as CompositeType)
var amqpSchemaA : CompositeType? = null
var amqpSchemaB : CompositeType? = null
@ -52,31 +57,266 @@ class Inheritance {
assert (amqpSchemaA != null)
assert (amqpSchemaB != null)
/*
* Just ensure the amqp schema matches what we want before we go messing
* around with the internals
*/
assertEquals(1, amqpSchemaA?.fields?.size)
assertEquals("a", amqpSchemaA!!.fields[0].name)
assertEquals("int", amqpSchemaA!!.fields[0].type)
assertEquals("int", amqpSchemaA.fields[0].type)
assertEquals(2, amqpSchemaB?.fields?.size)
assertEquals("a", amqpSchemaB!!.fields[0].name)
assertEquals("${this.javaClass.name}\$nestedInts\$A", amqpSchemaB!!.fields[0].type)
assertEquals("b", amqpSchemaB!!.fields[1].name)
assertEquals("int", amqpSchemaB!!.fields[1].type)
assertEquals(classTestName("A"), amqpSchemaB.fields[0].type)
assertEquals("b", amqpSchemaB.fields[1].name)
assertEquals("int", amqpSchemaB.fields[1].type)
var ccA = ClassCarpenter().build(amqpSchemaA.carpenterSchema())
var ccB = ClassCarpenter().build(amqpSchemaB.carpenterSchema())
val metaSchema = obj.second.schema.carpenterSchema()
/*
* Since A is known to the JVM we can't constuct B with and instance of the carpented A but
* need to use the defined one above
*/
val instanceA = ccA.constructors[0].newInstance(testA)
val instanceB = ccB.constructors[0].newInstance(A (testA), testB)
/* if we know all the classes there is nothing to really achieve here */
assert (metaSchema.carpenterSchemas.isEmpty())
assert (metaSchema.dependsOn.isEmpty())
assert (metaSchema.dependencies.isEmpty())
}
assertEquals (ccA.getMethod("getA").invoke(instanceA), amqpObj.a.a)
assertEquals ((ccB.getMethod("getA").invoke(instanceB) as A).a, amqpObj.a.a)
assertEquals (ccB.getMethod("getB").invoke(instanceB), amqpObj.b)
/* you cannot have an element of a composite class we know about
that is unknown as that should be impossible. If we have the containing
class in the class path then we must have all of it's constituent elements */
@Test(expected = UncarpentableException::class)
fun nestedIsUnknown () {
val testA = 10
val testB = 20
@CordaSerializable
data class A(override val a: Int) : I_
@CordaSerializable
data class B(val a: A, var b: Int)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
val amqpSchema = obj.second.schema.curruptName(listOf (classTestName ("A")))
assert(obj.first is B)
amqpSchema.carpenterSchema()
}
@Test
fun ParentIsUnknown () {
val testA = 10
val testB = 20
@CordaSerializable
data class A(override val a: Int) : I_
@CordaSerializable
data class B(val a: A, var b: Int)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
val amqpSchema = obj.second.schema.curruptName(listOf (classTestName ("B")))
val carpenterSchema = amqpSchema.carpenterSchema()
assertEquals (1, carpenterSchema.size)
val metaCarpenter = MetaCarpenter (carpenterSchema)
metaCarpenter.build()
assert (curruptName(classTestName("B")) in metaCarpenter.objects)
}
@Test
fun BothUnkown () {
val testA = 10
val testB = 20
@CordaSerializable
data class A(override val a: Int) : I_
@CordaSerializable
data class B(val a: A, var b: Int)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
val amqpSchema = obj.second.schema.curruptName(listOf (classTestName ("A"), classTestName ("B")))
val carpenterSchema = amqpSchema.carpenterSchema()
/* just verify we're in the expected initial state, A is carpentable, B is not because
it depends on A and the dependency chains are in place */
assertEquals (1, carpenterSchema.size)
assertEquals (curruptName(classTestName("A")), carpenterSchema.carpenterSchemas.first().name)
assertEquals (1, carpenterSchema.dependencies.size)
assert (curruptName(classTestName("B")) in carpenterSchema.dependencies)
assertEquals (1, carpenterSchema.dependsOn.size)
assert (curruptName(classTestName("A")) in carpenterSchema.dependsOn)
/* test meta carpenter lets us single step over the creation */
val metaCarpenter = TestMetaCarpenter (carpenterSchema)
/* we've built nothing so nothing should be there */
assertEquals (0, metaCarpenter.objects.size)
/* first iteration, carpent A, resolve deps and mark B as carpentable */
metaCarpenter.build()
/* one build iteration should have carpetned up A and worked out that B is now buildable
given it's depedencies have been satisfied */
assertTrue (curruptName(classTestName("A")) in metaCarpenter.objects)
assertFalse (curruptName(classTestName("B")) in metaCarpenter.objects)
assertEquals (1, carpenterSchema.carpenterSchemas.size)
assertEquals (curruptName(classTestName("B")), carpenterSchema.carpenterSchemas.first().name)
assertTrue (carpenterSchema.dependencies.isEmpty())
assertTrue (carpenterSchema.dependsOn.isEmpty())
/* second manual iteration, will carpent B */
metaCarpenter.build()
assert (curruptName(classTestName("A")) in metaCarpenter.objects)
assert (curruptName(classTestName("B")) in metaCarpenter.objects)
assertTrue (carpenterSchema.carpenterSchemas.isEmpty())
}
@Test(expected = UncarpentableException::class)
fun nestedIsUnkownInherited () {
val testA = 10
val testB = 20
val testC = 30
@CordaSerializable
open class A(val a: Int)
@CordaSerializable
class B(a: Int, var b: Int) : A (a)
@CordaSerializable
data class C(val b: B, var c: Int)
val c = C(B(testA, testB), testC)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(c))
assert(obj.first is C)
val amqpSchema = obj.second.schema.curruptName(listOf (classTestName ("A"), classTestName ("B")))
amqpSchema.carpenterSchema()
}
@Test(expected = UncarpentableException::class)
fun nestedIsUnknownInheritedUnkown () {
val testA = 10
val testB = 20
val testC = 30
@CordaSerializable
open class A(val a: Int)
@CordaSerializable
class B(a: Int, var b: Int) : A (a)
@CordaSerializable
data class C(val b: B, var c: Int)
val c = C(B(testA, testB), testC)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(c))
assert(obj.first is C)
val amqpSchema = obj.second.schema.curruptName(listOf (classTestName ("A"), classTestName ("B")))
amqpSchema.carpenterSchema()
}
@Test
fun parentsIsUnknownWithUnkownInheritedMember () {
val testA = 10
val testB = 20
val testC = 30
@CordaSerializable
open class A(val a: Int)
@CordaSerializable
class B(a: Int, var b: Int) : A (a)
@CordaSerializable
data class C(val b: B, var c: Int)
val c = C(B(testA, testB), testC)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(c))
assert(obj.first is C)
val amqpSchema = obj.second.schema.curruptName(listOf (classTestName ("A"), classTestName ("B")))
}
/*
* In this case B holds an element of Interface I_ which is an A but we don't know of A
* but we do know about I_
*/
@Test
fun nestedIsInterfaceToUnknown () {
val testA = 10
val testB = 20
@CordaSerializable
data class A(override val a: Int) : I_
@CordaSerializable
data class B(val a: A, var b: Int)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
}
@Test
fun nestedIsUnknownInterface() {
val testA = 10
val testB = 20
@CordaSerializable
data class A(override val a: Int) : I_
@CordaSerializable
data class B(val a: A, var b: Int)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
}
@Test
fun ParentsIsInterfaceToUnkown() {
val testA = 10
val testB = 20
@CordaSerializable
data class A(override val a: Int) : I_
@CordaSerializable
data class B(val a: A, var b: Int)
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
}
}

View File

@ -3,7 +3,9 @@ package net.corda.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.amqp.*
import org.junit.Test
import kotlin.test.assertEquals
import net.corda.carpenter.test.*
import net.corda.carpenter.MetaCarpenter
import kotlin.test.*
/*******************************************************************************************************/
@ -38,42 +40,11 @@ interface IIII {
/*******************************************************************************************************/
class InheritanceSchemaToClassCarpenterTests {
private var factory = SerializerFactory()
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
fun curruptName(name: String) = "${name}__carpenter"
/* given a list of class names work through the amqp envelope schema and alter any that
match in the fashion defined above */
fun curruptName (schema: Schema, names: List<String>) : Schema {
val types = schema.types
val newTypes : MutableList<TypeNotation> = mutableListOf()
for (type in types) {
val newName = if (type.name in names) curruptName (type.name) else type.name
val newProvides = type.provides.map {
it -> if (it in names) curruptName (it) else it
}
val newFields = (type as CompositeType).fields.map {
it -> if ( it.requires.isNotEmpty() && it.requires[0] in names)
it.copy (requires=listOf (curruptName (it.requires[0]))) else it
}
newTypes.add (type.copy (name=newName, provides=newProvides, fields=newFields))
}
return Schema (types=newTypes)
}
class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
@Test
fun interfaceParent1() {
val testJ = 20
val testName = "interfaceParent1"
class A(override val j: Int) : J
@ -95,17 +66,20 @@ class InheritanceSchemaToClassCarpenterTests {
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val curruptSchema = serSchema.curruptName (listOf (classTestName ("A")))
val l2 = curruptSchema.carpenterSchema()
assertEquals(1, l2.size)
val aSchema = l2.carpenterSchemas.find { it.name == curruptName(classTestName("A")) }
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(1, l2[0].interfaces.size)
assertEquals(net.corda.carpenter.J::class.java, l2[0].interfaces[0])
assertNotEquals (null, aSchema)
val aBuilder = ClassCarpenter().build(l2[0])
assertEquals(curruptName(classTestName("A")), aSchema!!.name)
assertEquals(1, aSchema.interfaces.size)
assertEquals(net.corda.carpenter.J::class.java, aSchema.interfaces[0])
val aBuilder = ClassCarpenter().build(aSchema)
val objJ = aBuilder.constructors[0].newInstance(testJ)
val j = objJ as J
@ -119,7 +93,6 @@ class InheritanceSchemaToClassCarpenterTests {
fun interfaceParent2() {
val testJ = 20
val testJJ = 40
val testName = "interfaceParent2"
class A(override val j: Int, val jj: Int) : J
@ -142,17 +115,22 @@ class InheritanceSchemaToClassCarpenterTests {
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val curruptSchema = serSchema.curruptName (listOf (classTestName("A")))
val aName = curruptName(classTestName("A"))
val l2 = curruptSchema.carpenterSchema()
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(1, l2[0].interfaces.size)
assertEquals(net.corda.carpenter.J::class.java, l2[0].interfaces[0])
val aSchema = l2.carpenterSchemas.find { it.name == aName }
val aBuilder = ClassCarpenter().build(l2[0])
assertNotEquals(null, aSchema)
assertEquals(aName, aSchema!!.name)
assertEquals(1, aSchema.interfaces.size)
assertEquals(net.corda.carpenter.J::class.java, aSchema.interfaces[0])
val aBuilder = ClassCarpenter().build(aSchema)
val objJ = aBuilder.constructors[0].newInstance(testJ, testJJ)
val j = objJ as J
@ -167,7 +145,6 @@ class InheritanceSchemaToClassCarpenterTests {
fun multipleInterfaces() {
val testI = 20
val testII = 40
val testName = "multipleInterfaces"
class A(override val i: Int, override val ii: Int) : I, II
@ -189,17 +166,22 @@ class InheritanceSchemaToClassCarpenterTests {
/* pretend we don't know the class we've been sent, i.e. it's unknown to the class loader, and thus
needs some carpentry */
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val l2 = curruptSchema.carpenterSchema()
val curruptSchema = serSchema.curruptName (listOf (classTestName ("A")))
val l2 = curruptSchema.carpenterSchema()
val aName = curruptName(classTestName("A"))
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(2, l2[0].interfaces.size)
assert (net.corda.carpenter.I::class.java in l2[0].interfaces)
assert (net.corda.carpenter.II::class.java in l2[0].interfaces)
val aSchema = l2.carpenterSchemas.find { it.name == aName }
val aBuilder = ClassCarpenter().build(l2[0])
assertNotEquals(null, aSchema)
assertEquals(aName, aSchema!!.name)
assertEquals(2, aSchema.interfaces.size)
assert (net.corda.carpenter.I::class.java in aSchema.interfaces)
assert (net.corda.carpenter.II::class.java in aSchema.interfaces)
val aBuilder = ClassCarpenter().build(aSchema)
val objA = aBuilder.constructors[0].newInstance(testI, testII)
val i = objA as I
@ -216,7 +198,6 @@ class InheritanceSchemaToClassCarpenterTests {
fun nestedInterfaces() {
val testI = 20
val testIII = 60
val testName = "nestedInterfaces"
class A(override val i: Int, override val iii : Int) : III
@ -235,17 +216,22 @@ class InheritanceSchemaToClassCarpenterTests {
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val curruptSchema = serSchema.curruptName (listOf (classTestName ("A")))
val l2 = curruptSchema.carpenterSchema()
val aName = curruptName(classTestName("A"))
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(2, l2[0].interfaces.size)
assert (net.corda.carpenter.I::class.java in l2[0].interfaces)
assert (net.corda.carpenter.III::class.java in l2[0].interfaces)
val aSchema = l2.carpenterSchemas.find { it.name == aName }
val aBuilder = ClassCarpenter().build(l2[0])
assertNotEquals(null, aSchema)
assertEquals(aName, aSchema!!.name)
assertEquals(2, aSchema.interfaces.size)
assert (net.corda.carpenter.I::class.java in aSchema.interfaces)
assert (net.corda.carpenter.III::class.java in aSchema.interfaces)
val aBuilder = ClassCarpenter().build(aSchema)
val objA = aBuilder.constructors[0].newInstance(testI, testIII)
val i = objA as I
@ -263,7 +249,6 @@ class InheritanceSchemaToClassCarpenterTests {
fun memberInterface() {
val testI = 25
val testIIII = 50
val testName = "memberInterface"
class A(override val i: Int) : I
class B(override val i : I, override val iiii : Int) : IIII
@ -285,17 +270,18 @@ class InheritanceSchemaToClassCarpenterTests {
*/
assertEquals(4, serSchema.types.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A",
"${this.javaClass.name}\$$testName\$B"))
val curruptSchema = serSchema.curruptName (listOf (classTestName ("A"), classTestName ("B")))
val cSchema = curruptSchema.carpenterSchema()
val aName = curruptName(classTestName("A"))
val bName = curruptName(classTestName("B"))
val cSchema = curruptSchema.carpenterSchema()
assertEquals(2, cSchema.size)
val aCarpenterSchema = cSchema.find { it.name == curruptName("${this.javaClass.name}\$$testName\$A") }
val bCarpenterSchema = cSchema.find { it.name == curruptName("${this.javaClass.name}\$$testName\$B") }
val aCarpenterSchema = cSchema.carpenterSchemas.find { it.name == aName }
val bCarpenterSchema = cSchema.carpenterSchemas.find { it.name == bName }
assert(aCarpenterSchema != null)
assert(bCarpenterSchema != null)
assertNotEquals (null, aCarpenterSchema)
assertNotEquals (null, bCarpenterSchema)
val cc = ClassCarpenter()
val cc2 = ClassCarpenter()
@ -317,13 +303,12 @@ class InheritanceSchemaToClassCarpenterTests {
bBuilder.constructors[0].newInstance(objA2, testIIII)
}
/* this time remove the nested interface from out set of known classes forcing us
to whittle it */
@Test
/* if we remove the neted interface we should get an error as it's impossible
to have a concrete class loaded without having access to all of it's elements */
@Test(expected = UncarpentableException::class)
fun memberInterface2() {
val testI = 25
val testIIII = 50
val testName = "memberInterface2"
class A(override val i: Int) : I
class B(override val i : I, override val iiii : Int) : IIII
@ -346,44 +331,185 @@ class InheritanceSchemaToClassCarpenterTests {
*/
assertEquals(4, serSchema.types.size)
val curruptSchema = curruptName (serSchema, listOf (
"${this.javaClass.name}\$$testName\$A",
"${this.javaClass.getPackage().name}.I"))
val carpenterSchema = curruptSchema.carpenterSchema()
val aCarpenterSchema = carpenterSchema.find { it.name == curruptName("${this.javaClass.name}\$$testName\$A") }
// val bCarpenterSchema = carpenterSchema.find { it.name == curruptName("${this.javaClass.getPackage().name}.I") }
val cc = ClassCarpenter()
val aBuilder = cc.build (aCarpenterSchema!!)
aBuilder.constructors[0].newInstance(testI)
/*
var cc2 = ClassCarpenter()
var bBuilder = cc.build(bCarpenterSchema!!)
val objB = bBuilder.constructors[0].newInstance(a, testIIII)
var aBuilder = cc.build(aCarpenterSchema!!)
val objA = aBuilder.constructors[0].newInstance(testI)
/* build a second B this time using our constructed instane of A and not the
local one we pre defined */
val objB2 = bBuilder.constructors[0].newInstance(objA, testIIII)
/* whittle and instantiate a different A with a new class loader */
var aBuilder2 = cc2.build(aCarpenterSchema!!)
val objA2 = aBuilder2.constructors[0].newInstance(testI)
val objB3 = bBuilder.constructors[0].newInstance(objA2, testIIII)
*/
/* ignore the return as we expect this to throw */
serSchema.curruptName (listOf (
classTestName("A"), "${this.javaClass.`package`.name}.I")).carpenterSchema()
}
@Test
fun interfaceAndImplementation() {
val testI = 25
class A(override val i: Int) : I
val a = A(testI)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
assert(obj.first is A)
val serSchema = obj.second.schema
/*
* The classes we're expecting to find:
* class A
* class A's interface (class I)
*/
assertEquals(2, serSchema.types.size)
val amqpSchema = serSchema.curruptName (listOf (classTestName("A"), "${this.javaClass.`package`.name}.I"))
val aName = curruptName (classTestName("A"))
val iName = curruptName ("${this.javaClass.`package`.name}.I")
val carpenterSchema = amqpSchema.carpenterSchema()
/* whilst there are two unknown classes within the envelope A depends on I so we can't construct a
schema for A until we have for I */
assertEquals (1, carpenterSchema.size)
assertNotEquals (null, carpenterSchema.carpenterSchemas.find { it.name == iName })
/* since we can't build A it should list I as a dependency*/
assert (aName in carpenterSchema.dependencies)
assertEquals (1, carpenterSchema.dependencies[aName]!!.second.size)
assertEquals (iName, carpenterSchema.dependencies[aName]!!.second[0])
/* and conversly I should have A listed as a dependent */
assert(iName in carpenterSchema.dependsOn)
assertEquals(1, carpenterSchema.dependsOn[iName]!!.size)
assertEquals(aName, carpenterSchema.dependsOn[iName]!![0])
val mc = MetaCarpenter (carpenterSchema)
mc.build()
assertEquals (0, mc.schemas.carpenterSchemas.size)
assertEquals (0, mc.schemas.dependencies.size)
assertEquals (0, mc.schemas.dependsOn.size)
assertEquals (2, mc.objects.size)
assert (aName in mc.objects)
assert (iName in mc.objects)
mc.objects[aName]!!.constructors[0].newInstance(testI)
}
@Test
fun twoInterfacesAndImplementation() {
val testI = 69
val testII = 96
class A(override val i: Int, override val ii: Int) : I, II
val a = A(testI, testII)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
val amqpSchema = obj.second.schema.curruptName(listOf(
classTestName("A"),
"${this.javaClass.`package`.name}.I",
"${this.javaClass.`package`.name}.II"))
val aName = curruptName (classTestName("A"))
val iName = curruptName ("${this.javaClass.`package`.name}.I")
val iiName = curruptName ("${this.javaClass.`package`.name}.II")
val carpenterSchema = amqpSchema.carpenterSchema()
/* there is nothing preventing us from carpenting up the two interfaces so
our initial list should contain both interface with A being dependent on both
and each having A as a dependent */
assertEquals (2, carpenterSchema.carpenterSchemas.size)
assertNotNull (carpenterSchema.carpenterSchemas.find { it.name == iName })
assertNotNull (carpenterSchema.carpenterSchemas.find { it.name == iiName })
assertNull (carpenterSchema.carpenterSchemas.find { it.name == aName })
assert (iName in carpenterSchema.dependsOn)
assertEquals (1, carpenterSchema.dependsOn[iName]?.size)
assertNotNull (carpenterSchema.dependsOn[iName]?.find ( { it == aName }))
assert (iiName in carpenterSchema.dependsOn)
assertEquals (1, carpenterSchema.dependsOn[iiName]?.size)
assertNotNull (carpenterSchema.dependsOn[iiName]?.find { it == aName })
assert (aName in carpenterSchema.dependencies)
assertEquals (2, carpenterSchema.dependencies[aName]!!.second.size)
assertNotNull (carpenterSchema.dependencies[aName]!!.second.find { it == iName })
assertNotNull (carpenterSchema.dependencies[aName]!!.second.find { it == iiName })
val mc = MetaCarpenter (carpenterSchema)
mc.build()
assertEquals (0, mc.schemas.carpenterSchemas.size)
assertEquals (0, mc.schemas.dependencies.size)
assertEquals (0, mc.schemas.dependsOn.size)
assertEquals (3, mc.objects.size)
assert (aName in mc.objects)
assert (iName in mc.objects)
assert (iiName in mc.objects)
}
@Test
fun nestedInterfacesAndImplementation() {
val testI = 7
val testIII = 11
class A(override val i: Int, override val iii : Int) : III
val a = A(testI, testIII)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
val amqpSchema = obj.second.schema.curruptName(listOf(
classTestName("A"),
"${this.javaClass.`package`.name}.I",
"${this.javaClass.`package`.name}.III"))
val aName = curruptName (classTestName("A"))
val iName = curruptName ("${this.javaClass.`package`.name}.I")
val iiiName = curruptName ("${this.javaClass.`package`.name}.III")
val carpenterSchema = amqpSchema.carpenterSchema()
/* Since A depends on III and III extends I we will have to construct them
* in that reverse order (I -> III -> A) */
assertEquals (1, carpenterSchema.carpenterSchemas.size)
assertNotNull (carpenterSchema.carpenterSchemas.find { it.name == iName })
assertNull (carpenterSchema.carpenterSchemas.find { it.name == iiiName })
assertNull (carpenterSchema.carpenterSchemas.find { it.name == aName })
/* I has III as a direct dependent and A as an indirect one */
assert (iName in carpenterSchema.dependsOn)
assertEquals (2, carpenterSchema.dependsOn[iName]?.size)
assertNotNull (carpenterSchema.dependsOn[iName]?.find ( { it == iiiName }))
assertNotNull (carpenterSchema.dependsOn[iName]?.find ( { it == aName }))
/* III has A as a dependent */
assert (iiiName in carpenterSchema.dependsOn)
assertEquals (1, carpenterSchema.dependsOn[iiiName]?.size)
assertNotNull (carpenterSchema.dependsOn[iiiName]?.find { it == aName })
/* converly III depends on I */
assert (iiiName in carpenterSchema.dependencies)
assertEquals (1, carpenterSchema.dependencies[iiiName]!!.second.size)
assertNotNull (carpenterSchema.dependencies[iiiName]!!.second.find { it == iName })
/* and A depends on III and I*/
assert (aName in carpenterSchema.dependencies)
assertEquals (2, carpenterSchema.dependencies[aName]!!.second.size)
assertNotNull (carpenterSchema.dependencies[aName]!!.second.find { it == iiiName })
assertNotNull (carpenterSchema.dependencies[aName]!!.second.find { it == iName })
val mc = MetaCarpenter (carpenterSchema)
mc.build()
assertEquals (0, mc.schemas.carpenterSchemas.size)
assertEquals (0, mc.schemas.dependencies.size)
assertEquals (0, mc.schemas.dependsOn.size)
assertEquals (3, mc.objects.size)
assert (aName in mc.objects)
assert (iName in mc.objects)
assert (iiiName in mc.objects)
}
}

View File

@ -1,14 +1,14 @@
package net.corda.carpenter
import net.corda.carpenter.test.AmqpCarpenterBase
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.amqp.*
import net.corda.core.serialization.carpenter.CarpenterSchemas
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
class MultiMemberCompositeSchemaToClassCarpenterTests {
private var factory = SerializerFactory()
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
@Test
fun twoInts() {
@ -38,7 +38,15 @@ class MultiMemberCompositeSchemaToClassCarpenterTests {
assertEquals("b", amqpSchema.fields[1].name)
assertEquals("int", amqpSchema.fields[1].type)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
assertEquals (1, carpenterSchema.size)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A") }
assertNotEquals (null, aSchema)
val pinochio = ClassCarpenter().build(aSchema!!)
val p = pinochio.constructors[0].newInstance(testA, testB)
@ -54,7 +62,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests {
@CordaSerializable
data class A(val a: Int, val b: String)
var a = A(testA, testB)
val a = A(testA, testB)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
@ -66,7 +74,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests {
assertEquals(1, obj.second.schema.types.size)
assert(obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
val amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals(2, amqpSchema.fields.size)
assertEquals("a", amqpSchema.fields[0].name)
@ -74,7 +82,15 @@ class MultiMemberCompositeSchemaToClassCarpenterTests {
assertEquals("b", amqpSchema.fields[1].name)
assertEquals("string", amqpSchema.fields[1].type)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
assertEquals (1, carpenterSchema.size)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName("A") }
assertNotEquals(null, aSchema)
val pinochio = ClassCarpenter().build(aSchema!!)
val p = pinochio.constructors[0].newInstance(testA, testB)

View File

@ -1,16 +1,14 @@
package net.corda.carpenter
import net.corda.carpenter.test.AmqpCarpenterBase
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.ClassCarpenterSchema
import net.corda.core.serialization.amqp.*
import net.corda.core.serialization.carpenter.CarpenterSchemas
import org.junit.Test
import kotlin.test.assertEquals
class SingleMemberCompositeSchemaToClassCarpenterTests {
private var factory = SerializerFactory()
fun serialise (clazz : Any) = SerializationOutput(factory).serialize(clazz)
class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
@Test
fun singleInteger() {
@ -35,7 +33,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("int", amqpSchema.fields[0].type)
val pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A") }!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
@ -48,7 +50,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
@CordaSerializable
data class A(val a : String)
var a = A (test)
val a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
@ -59,13 +61,13 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
val amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("string", amqpSchema.fields[0].type)
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A") }!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
@ -79,9 +81,9 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
@CordaSerializable
data class A(val a : Char)
var a = A (test)
val a = A(test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
assert (obj.first is A)
val amqpObj = obj.first as A
@ -91,13 +93,19 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
val amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("char", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val carpenterSchema = CarpenterSchema.newInstance()
amqpSchema.carpenterSchema(carpenterSchema = carpenterSchema, force = true)
assert (classTestName ("A") in carpenterSchema.carpenterSchemas)
val aSchema = carpenterSchema.carpenterSchemas[classTestName ("A")]!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
@ -129,8 +137,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("long", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A") }!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
@ -160,8 +171,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("short", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A")}!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
@ -207,7 +221,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
@CordaSerializable
data class A(val a : Double)
var a = A (test)
val a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
@ -218,14 +232,17 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
val amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("double", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A")}!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
@ -233,12 +250,12 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
@Test
fun singleFloat() {
val test = 10.0F
val test : Float = 10.0F
@CordaSerializable
data class A(val a : Float)
var a = A (test)
val a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
@ -249,16 +266,19 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
val amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("float", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val carpenterSchema = CarpenterSchemas.newInstance()
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName ("A") }!!
val pinochio = ClassCarpenter().build(aSchema)
val p = pinochio.constructors[0].newInstance (test)
// assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
}