mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
Review comments
* Move all alterations on the amqp schema object out of the actual amqp/Schema file and have them live in the carpenter as extension functions * Move carpenter exceptions to their own file * Rename the schema name corrupter to the name mangler * reduce whitespace * alter comment style
This commit is contained in:
parent
5ab26dd275
commit
86902e6356
@ -15,11 +15,8 @@ import java.lang.reflect.Type
|
||||
import java.lang.reflect.TypeVariable
|
||||
import java.util.*
|
||||
|
||||
import net.corda.core.serialization.carpenter.CarpenterSchemas
|
||||
import net.corda.core.serialization.carpenter.Schema as CarpenterSchema
|
||||
import net.corda.core.serialization.carpenter.Field as CarpenterField
|
||||
import net.corda.core.serialization.carpenter.CarpenterSchemaFactory
|
||||
import net.corda.core.serialization.carpenter.FieldFactory
|
||||
|
||||
// TODO: get an assigned number as per AMQP spec
|
||||
val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000
|
||||
@ -29,24 +26,6 @@ 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
|
||||
@ -111,18 +90,6 @@ data class Schema(val types: List<TypeNotation>) : DescribedType {
|
||||
override fun getDescribed(): Any = listOf(types)
|
||||
|
||||
override fun toString(): String = types.joinToString("\n")
|
||||
|
||||
fun carpenterSchema(loaders : List<ClassLoader> = listOf<ClassLoader>(ClassLoader.getSystemClassLoader()))
|
||||
: CarpenterSchemas
|
||||
{
|
||||
val rtn = CarpenterSchemas.newInstance()
|
||||
|
||||
types.filterIsInstance<CompositeType>().forEach {
|
||||
it.carpenterSchema(classLoaders = loaders, carpenterSchemas = rtn)
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
}
|
||||
|
||||
data class Descriptor(val name: String?, val code: UnsignedLong? = null) : DescribedType {
|
||||
@ -205,29 +172,6 @@ data class Field(val name: String, val type: String, val requires: List<String>,
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun getTypeAsClass(
|
||||
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!!
|
||||
"*" -> classLoaders.loadIfExists(requires[0])
|
||||
else -> classLoaders.loadIfExists(type)
|
||||
}
|
||||
|
||||
fun validateType(
|
||||
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())
|
||||
) = when (type) {
|
||||
"int", "string", "short", "long", "char", "boolean", "double", "float" -> true
|
||||
"*" -> classLoaders.exists(requires[0])
|
||||
else -> classLoaders.exists (type)
|
||||
}
|
||||
|
||||
fun typeAsString() = if (type =="*") requires[0] else type
|
||||
}
|
||||
|
||||
@ -294,85 +238,6 @@ data class CompositeType(override val name: String, override val label: String?,
|
||||
sb.append("</type>")
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* if we can load the class then we MUST know about all of it's composite elements
|
||||
*/
|
||||
private fun validateKnown (
|
||||
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader()))
|
||||
{
|
||||
fields.forEach {
|
||||
if (!it.validateType(classLoaders)) throw UncarpentableException (name, it.name, it.type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* based upon this AMQP schema either
|
||||
* a) add the corespending carpenter schema to the [carpenterSchemas] param
|
||||
* b) add the class to the dependency tree in [carpenterSchemas] if it cannot be instantiated
|
||||
* at this time
|
||||
*
|
||||
* @param classLoaders list of classLoaders, defaulting toe the system class loader, that might
|
||||
* be used to load objects
|
||||
* @param carpenterSchemas structure that holds the dependency tree and list of classes that
|
||||
* need constructing
|
||||
* @param force by default a schema is not added to [carpenterSchemas] if it already exists
|
||||
* on the class path. For testing purposes schema generation can be forced
|
||||
*/
|
||||
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)
|
||||
|
||||
if (!force) return
|
||||
}
|
||||
|
||||
val providesList = mutableListOf<Class<*>>()
|
||||
|
||||
var isInterface = false
|
||||
var isCreatable = true
|
||||
|
||||
provides.forEach {
|
||||
if (name == 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, CarpenterField> = mutableMapOf()
|
||||
|
||||
fields.forEach {
|
||||
try {
|
||||
m[it.name] = FieldFactory.newInstance(it.mandatory, 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class RestrictedType(override val name: String, override val label: String?, override val provides: List<String>, val source: String, override val descriptor: Descriptor, val choices: List<Choice>) : TypeNotation() {
|
||||
|
@ -0,0 +1,127 @@
|
||||
package net.corda.core.serialization.carpenter
|
||||
|
||||
import net.corda.core.serialization.amqp.Schema as AMQPSchema
|
||||
import net.corda.core.serialization.amqp.Field as AMQPField
|
||||
import net.corda.core.serialization.amqp.CompositeType
|
||||
|
||||
fun AMQPSchema.carpenterSchema(
|
||||
loaders : List<ClassLoader> = listOf<ClassLoader>(ClassLoader.getSystemClassLoader()))
|
||||
: CarpenterSchemas {
|
||||
val rtn = CarpenterSchemas.newInstance()
|
||||
|
||||
types.filterIsInstance<CompositeType>().forEach {
|
||||
it.carpenterSchema(classLoaders = loaders, carpenterSchemas = rtn)
|
||||
}
|
||||
|
||||
return rtn
|
||||
}
|
||||
|
||||
/**
|
||||
* if we can load the class then we MUST know about all of it's composite elements
|
||||
*/
|
||||
private fun CompositeType.validateKnown (
|
||||
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())){
|
||||
fields.forEach {
|
||||
if (!it.validateType(classLoaders)) throw UncarpentableException (name, it.name, it.type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* based upon this AMQP schema either
|
||||
* a) add the corresponding carpenter schema to the [carpenterSchemas] param
|
||||
* b) add the class to the dependency tree in [carpenterSchemas] if it cannot be instantiated
|
||||
* at this time
|
||||
*
|
||||
* @param classLoaders list of classLoaders, defaulting toe the system class loader, that might
|
||||
* be used to load objects
|
||||
* @param carpenterSchemas structure that holds the dependency tree and list of classes that
|
||||
* need constructing
|
||||
* @param force by default a schema is not added to [carpenterSchemas] if it already exists
|
||||
* on the class path. For testing purposes schema generation can be forced
|
||||
*/
|
||||
fun CompositeType.carpenterSchema(
|
||||
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader()),
|
||||
carpenterSchemas : CarpenterSchemas,
|
||||
force : Boolean = false) {
|
||||
if (classLoaders.exists(name)) {
|
||||
validateKnown(classLoaders)
|
||||
if (!force) return
|
||||
}
|
||||
|
||||
val providesList = mutableListOf<Class<*>>()
|
||||
|
||||
var isInterface = false
|
||||
var isCreatable = true
|
||||
|
||||
provides.forEach {
|
||||
if (name == 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, Field> = mutableMapOf()
|
||||
|
||||
fields.forEach {
|
||||
try {
|
||||
m[it.name] = FieldFactory.newInstance(it.mandatory, 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))
|
||||
}
|
||||
}
|
||||
|
||||
fun AMQPField.getTypeAsClass(
|
||||
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!!
|
||||
"*" -> classLoaders.loadIfExists(requires[0])
|
||||
else -> classLoaders.loadIfExists(type)
|
||||
}
|
||||
|
||||
fun AMQPField.validateType(
|
||||
classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())
|
||||
) = when (type) {
|
||||
"int", "string", "short", "long", "char", "boolean", "double", "float" -> true
|
||||
"*" -> classLoaders.exists(requires[0])
|
||||
else -> classLoaders.exists (type)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
@ -9,8 +9,6 @@ import java.lang.Character.isJavaIdentifierStart
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Any object that implements this interface is expected to expose its own fields via the [get] method, exactly
|
||||
* as if `this.class.getMethod("get" + name.capitalize()).invoke(this)` had been called. It is intended as a more
|
||||
@ -20,14 +18,10 @@ interface SimpleFieldAccess {
|
||||
operator fun get(name: String): Any?
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
|
||||
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* A class carpenter generates JVM bytecodes for a class given a schema and then loads it into a sub-classloader.
|
||||
* The generated classes have getters, a toString method and implement a simple property access interface. The
|
||||
@ -91,7 +85,8 @@ class ClassCarpenter {
|
||||
* Generate bytecode for the given schema and load into the JVM. The returned class object can be used to
|
||||
* construct instances of the generated class.
|
||||
*
|
||||
* @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)
|
||||
* @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: Schema): Class<*> {
|
||||
validateSchema(schema)
|
||||
@ -257,9 +252,7 @@ class ClassCarpenter {
|
||||
visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
|
||||
} else {
|
||||
var slot = 1
|
||||
for (fieldType in superclassFields.values)
|
||||
slot += load(slot, fieldType)
|
||||
//val superDesc = schema.superclass.descriptorsIncludingSuperclasses().values.joinToString("")
|
||||
superclassFields.values.forEach { slot += load(slot, it) }
|
||||
val superDesc = sc.descriptorsIncludingSuperclasses().values.joinToString("")
|
||||
visitMethodInsn(INVOKESPECIAL, sc.name.jvm, "<init>", "($superDesc)V", false)
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.corda.core.serialization.carpenter
|
||||
|
||||
|
||||
class DuplicateNameException : RuntimeException (
|
||||
"An attempt was made to register two classes with the same name within the same ClassCarpenter namespace.")
|
||||
|
||||
class InterfaceMismatchException(msg: String) : RuntimeException(msg)
|
||||
|
||||
class NullablePrimitiveException(msg: String) : RuntimeException(msg)
|
||||
|
||||
class UncarpentableException (name: String, field: String, type: String) :
|
||||
Exception ("Class $name is loadable yet contains field $field of unknown type $type")
|
||||
|
@ -3,8 +3,6 @@ package net.corda.core.serialization.carpenter
|
||||
import net.corda.core.serialization.amqp.CompositeType
|
||||
import net.corda.core.serialization.amqp.TypeNotation
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Generated from an AMQP schema this class represents the classes unknown to the deserialiser and that thusly
|
||||
* require carpenting up in bytecode form. This is a multi step process as carpenting one object may be depedent
|
||||
@ -27,8 +25,7 @@ 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>>)
|
||||
{
|
||||
val dependsOn : MutableMap<String, MutableList<String>>) {
|
||||
companion object CarpenterSchemaConstructor {
|
||||
fun newInstance(): CarpenterSchemas {
|
||||
return CarpenterSchemas(
|
||||
@ -47,8 +44,6 @@ data class CarpenterSchemas (
|
||||
get() = carpenterSchemas.size
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Take a dependency tree of [CarpenterSchemas] and reduce it to zero by carpenting those classes that
|
||||
* require it. As classes are carpented check for depdency resolution, if now free generate a [Schema] for
|
||||
@ -65,15 +60,15 @@ abstract class MetaCarpenterBase (val schemas : CarpenterSchemas) {
|
||||
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 */
|
||||
// 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 */
|
||||
// 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> (
|
||||
@ -87,8 +82,6 @@ abstract class MetaCarpenterBase (val schemas : CarpenterSchemas) {
|
||||
abstract fun build()
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
class MetaCarpenter (schemas : CarpenterSchemas) : MetaCarpenterBase (schemas) {
|
||||
override fun build() {
|
||||
while (schemas.carpenterSchemas.isNotEmpty()) {
|
||||
@ -98,8 +91,6 @@ class MetaCarpenter (schemas : CarpenterSchemas) : MetaCarpenterBase (schemas) {
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
class TestMetaCarpenter (schemas : CarpenterSchemas) : MetaCarpenterBase (schemas) {
|
||||
override fun build() {
|
||||
if (schemas.carpenterSchemas.isEmpty()) return
|
||||
@ -107,4 +98,3 @@ class TestMetaCarpenter (schemas : CarpenterSchemas) : MetaCarpenterBase (schema
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
@ -264,7 +264,6 @@ class ClassCarpenterTest {
|
||||
mapOf("a" to NonNullableField (Int::class.java)))
|
||||
|
||||
val clazz = cc.build(schema)
|
||||
|
||||
val a : Int? = null
|
||||
clazz.constructors[0].newInstance(a)
|
||||
}
|
||||
@ -364,7 +363,6 @@ class ClassCarpenterTest {
|
||||
val clazz = cc.build(schema)
|
||||
|
||||
val i = clazz.constructors[0].newInstance(arrayOf(1, 2, 3)) as SimpleFieldAccess
|
||||
|
||||
val arr = clazz.getMethod("getA").invoke(i)
|
||||
|
||||
assertEquals(1, (arr as Array<Int>)[0])
|
||||
@ -384,14 +382,12 @@ class ClassCarpenterTest {
|
||||
"c" to Int::class.java).mapValues { NonNullableField(it.value) })
|
||||
|
||||
val clazz = cc.build(schema)
|
||||
|
||||
val i = clazz.constructors[0].newInstance(2, intArrayOf(4, 8), 16) as SimpleFieldAccess
|
||||
|
||||
assertEquals(2, clazz.getMethod("getA").invoke(i))
|
||||
assertEquals(4, (clazz.getMethod("getB").invoke(i) as IntArray)[0])
|
||||
assertEquals(8, (clazz.getMethod("getB").invoke(i) as IntArray)[1])
|
||||
assertEquals(16, clazz.getMethod("getC").invoke(i))
|
||||
|
||||
assertEquals("$className{a=2, b=[4, 8], c=16}", i.toString())
|
||||
}
|
||||
|
||||
@ -414,7 +410,6 @@ class ClassCarpenterTest {
|
||||
assertEquals(4, (clazz.getMethod("getC").invoke(i) as IntArray)[0])
|
||||
assertEquals(5, (clazz.getMethod("getC").invoke(i) as IntArray)[1])
|
||||
assertEquals(6, (clazz.getMethod("getC").invoke(i) as IntArray)[2])
|
||||
|
||||
assertEquals("$className{a=[1, 2], b=3, c=[4, 5, 6]}", i.toString())
|
||||
}
|
||||
|
||||
@ -454,7 +449,6 @@ class ClassCarpenterTest {
|
||||
"and on the side",
|
||||
arrayOf("some pickles", "some fries"))
|
||||
|
||||
|
||||
val arr1 = clazz.getMethod("getA").invoke(i) as Array<String>
|
||||
val arr2 = clazz.getMethod("getC").invoke(i) as Array<String>
|
||||
|
||||
@ -477,16 +471,12 @@ class ClassCarpenterTest {
|
||||
val clazz = cc.build(schema)
|
||||
|
||||
assertEquals (2, clazz.declaredFields.size)
|
||||
|
||||
assertEquals (1, clazz.getDeclaredField("a").annotations.size)
|
||||
assertEquals (javax.annotation.Nullable::class.java, clazz.getDeclaredField("a").annotations[0].annotationClass.java)
|
||||
|
||||
assertEquals (1, clazz.getDeclaredField("b").annotations.size)
|
||||
assertEquals (javax.annotation.Nonnull::class.java, clazz.getDeclaredField("b").annotations[0].annotationClass.java)
|
||||
|
||||
assertEquals (1, clazz.getMethod("getA").annotations.size)
|
||||
assertEquals (javax.annotation.Nullable::class.java, clazz.getMethod("getA").annotations[0].annotationClass.java)
|
||||
|
||||
assertEquals (1, clazz.getMethod("getB").annotations.size)
|
||||
assertEquals (javax.annotation.Nonnull::class.java, clazz.getMethod("getB").annotations[0].annotationClass.java)
|
||||
}
|
||||
@ -503,5 +493,4 @@ class ClassCarpenterTest {
|
||||
assertNotEquals(null, descriptors.find { it.name == "a" })
|
||||
assertNotEquals(null, descriptors.find { it.name == "class" })
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,32 +7,25 @@ import net.corda.core.serialization.amqp.TypeNotation
|
||||
import net.corda.core.serialization.amqp.SerializerFactory
|
||||
import net.corda.core.serialization.amqp.SerializationOutput
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
fun mangleName(name: String) = "${name}__carpenter"
|
||||
|
||||
fun corruptName(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.corruptName(names: List<String>): Schema {
|
||||
/**
|
||||
* given a list of class names work through the amqp envelope schema and alter any that
|
||||
* match in the fashion defined above
|
||||
*/
|
||||
fun Schema.mangleName(names: List<String>): Schema {
|
||||
val newTypes: MutableList<TypeNotation> = mutableListOf()
|
||||
|
||||
for (type in types) {
|
||||
val newName = if (type.name in names) corruptName(type.name) else type.name
|
||||
|
||||
val newProvides = type.provides.map {
|
||||
it ->
|
||||
if (it in names) corruptName(it) else it
|
||||
}
|
||||
|
||||
val newName = if (type.name in names) mangleName(type.name) else type.name
|
||||
val newProvides = type.provides.map { it -> if (it in names) mangleName(it) else it }
|
||||
val newFields = mutableListOf<Field>()
|
||||
|
||||
(type as CompositeType).fields.forEach {
|
||||
val fieldType = if (it.type in names) corruptName(it.type) else it.type
|
||||
|
||||
val requires = if (it.requires.isNotEmpty() && (it.requires[0] in names))
|
||||
listOf(corruptName(it.requires[0])) else it.requires
|
||||
val fieldType = if (it.type in names) mangleName(it.type) else it.type
|
||||
val requires =
|
||||
if (it.requires.isNotEmpty() && (it.requires[0] in names)) listOf(mangleName(it.requires[0]))
|
||||
else it.requires
|
||||
|
||||
newFields.add(it.copy(type = fieldType, requires = requires))
|
||||
}
|
||||
@ -43,8 +36,6 @@ fun Schema.corruptName(names: List<String>): Schema {
|
||||
return Schema(types = newTypes)
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************/
|
||||
|
||||
open class AmqpCarpenterBase {
|
||||
var factory = SerializerFactory()
|
||||
|
||||
|
@ -14,11 +14,7 @@ interface I_ {
|
||||
val a: Int
|
||||
}
|
||||
|
||||
/*
|
||||
* Where a class has a member that is also a composite type or interface
|
||||
*/
|
||||
class CompositeMembers : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun bothKnown() {
|
||||
val testA = 10
|
||||
@ -31,7 +27,6 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
data class B(val a: A, var b: Int)
|
||||
|
||||
val b = B(A(testA), testB)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
|
||||
assert(obj.obj is B)
|
||||
@ -57,10 +52,8 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
assert(amqpSchemaA != null)
|
||||
assert(amqpSchemaB != null)
|
||||
|
||||
/*
|
||||
* Just ensure the amqp schema matches what we want before we go messing
|
||||
* around with the internals
|
||||
*/
|
||||
// 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)
|
||||
@ -73,15 +66,15 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
|
||||
val metaSchema = obj.envelope.schema.carpenterSchema()
|
||||
|
||||
/* if we know all the classes there is nothing to really achieve here */
|
||||
// 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())
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
// 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
|
||||
@ -96,7 +89,7 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
val b = B(A(testA), testB)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf (classTestName ("A")))
|
||||
val amqpSchema = obj.envelope.schema.mangleName(listOf (classTestName ("A")))
|
||||
|
||||
assert(obj.obj is B)
|
||||
|
||||
@ -115,13 +108,11 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
data class B(val a: A, var b: Int)
|
||||
|
||||
val b = B(A(testA), testB)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
|
||||
assert(obj.obj is B)
|
||||
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf(classTestName("B")))
|
||||
|
||||
val amqpSchema = obj.envelope.schema.mangleName(listOf(classTestName("B")))
|
||||
val carpenterSchema = amqpSchema.carpenterSchema()
|
||||
|
||||
assertEquals(1, carpenterSchema.size)
|
||||
@ -130,11 +121,11 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
|
||||
metaCarpenter.build()
|
||||
|
||||
assert(corruptName(classTestName("B")) in metaCarpenter.objects)
|
||||
assert(mangleName(classTestName("B")) in metaCarpenter.objects)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun BothUnkown() {
|
||||
fun BothUnknown() {
|
||||
val testA = 10
|
||||
val testB = 20
|
||||
|
||||
@ -145,52 +136,50 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
data class B(val a: A, var b: Int)
|
||||
|
||||
val b = B(A(testA), testB)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
|
||||
assert(obj.obj is B)
|
||||
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf(classTestName("A"), classTestName("B")))
|
||||
|
||||
val amqpSchema = obj.envelope.schema.mangleName(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 */
|
||||
// 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(corruptName(classTestName("A")), carpenterSchema.carpenterSchemas.first().name)
|
||||
assertEquals(mangleName(classTestName("A")), carpenterSchema.carpenterSchemas.first().name)
|
||||
assertEquals(1, carpenterSchema.dependencies.size)
|
||||
assert(corruptName(classTestName("B")) in carpenterSchema.dependencies)
|
||||
assert(mangleName(classTestName("B")) in carpenterSchema.dependencies)
|
||||
assertEquals(1, carpenterSchema.dependsOn.size)
|
||||
assert(corruptName(classTestName("A")) in carpenterSchema.dependsOn)
|
||||
assert(mangleName(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 */
|
||||
// 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(corruptName(classTestName("A")) in metaCarpenter.objects)
|
||||
assertFalse(corruptName(classTestName("B")) in metaCarpenter.objects)
|
||||
// one build iteration should have carpetned up A and worked out that B is now buildable
|
||||
// given it's depedencies have been satisfied
|
||||
assertTrue(mangleName(classTestName("A")) in metaCarpenter.objects)
|
||||
assertFalse(mangleName(classTestName("B")) in metaCarpenter.objects)
|
||||
|
||||
assertEquals(1, carpenterSchema.carpenterSchemas.size)
|
||||
assertEquals(corruptName(classTestName("B")), carpenterSchema.carpenterSchemas.first().name)
|
||||
assertEquals(mangleName(classTestName("B")), carpenterSchema.carpenterSchemas.first().name)
|
||||
assertTrue(carpenterSchema.dependencies.isEmpty())
|
||||
assertTrue(carpenterSchema.dependsOn.isEmpty())
|
||||
|
||||
/* second manual iteration, will carpent B */
|
||||
// second manual iteration, will carpent B
|
||||
metaCarpenter.build()
|
||||
assert(corruptName(classTestName("A")) in metaCarpenter.objects)
|
||||
assert(corruptName(classTestName("B")) in metaCarpenter.objects)
|
||||
assert(mangleName(classTestName("A")) in metaCarpenter.objects)
|
||||
assert(mangleName(classTestName("B")) in metaCarpenter.objects)
|
||||
|
||||
// and we must be finished
|
||||
assertTrue(carpenterSchema.carpenterSchemas.isEmpty())
|
||||
}
|
||||
|
||||
@Test(expected = UncarpentableException::class)
|
||||
@Suppress("UNUSED")
|
||||
fun nestedIsUnkownInherited() {
|
||||
val testA = 10
|
||||
val testB = 20
|
||||
@ -206,18 +195,18 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
data class C(val b: B, var c: Int)
|
||||
|
||||
val c = C(B(testA, testB), testC)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(c))
|
||||
|
||||
assert(obj.obj is C)
|
||||
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf(classTestName("A"), classTestName("B")))
|
||||
val amqpSchema = obj.envelope.schema.mangleName(listOf(classTestName("A"), classTestName("B")))
|
||||
|
||||
amqpSchema.carpenterSchema()
|
||||
}
|
||||
|
||||
@Test(expected = UncarpentableException::class)
|
||||
fun nestedIsUnknownInheritedUnkown() {
|
||||
@Suppress("UNUSED")
|
||||
fun nestedIsUnknownInheritedUnknown() {
|
||||
val testA = 10
|
||||
val testB = 20
|
||||
val testC = 30
|
||||
@ -232,16 +221,16 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
data class C(val b: B, var c: Int)
|
||||
|
||||
val c = C(B(testA, testB), testC)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(c))
|
||||
|
||||
assert(obj.obj is C)
|
||||
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf(classTestName("A"), classTestName("B")))
|
||||
val amqpSchema = obj.envelope.schema.mangleName(listOf(classTestName("A"), classTestName("B")))
|
||||
|
||||
amqpSchema.carpenterSchema()
|
||||
}
|
||||
|
||||
@Suppress("UNUSED")
|
||||
@Test(expected = UncarpentableException::class)
|
||||
fun parentsIsUnknownWithUnknownInheritedMember() {
|
||||
val testA = 10
|
||||
@ -258,18 +247,14 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
data class C(val b: B, var c: Int)
|
||||
|
||||
val c = C(B(testA, testB), testC)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(c))
|
||||
|
||||
assert(obj.obj is C)
|
||||
|
||||
val carpenterSchema = obj.envelope.schema.corruptName(listOf(classTestName("A"), classTestName("B")))
|
||||
|
||||
val carpenterSchema = obj.envelope.schema.mangleName(listOf(classTestName("A"), classTestName("B")))
|
||||
TestMetaCarpenter(carpenterSchema.carpenterSchema())
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* TODO serializer doesn't support inheritnace at the moment, when it does this should work
|
||||
@Test
|
||||
@ -284,19 +269,17 @@ class CompositeMembers : AmqpCarpenterBase() {
|
||||
class B(override val a: Int, val b: Int) : A (a)
|
||||
|
||||
val b = B(testA, testB)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
|
||||
assert(obj.obj is B)
|
||||
|
||||
val carpenterSchema = obj.envelope.schema.corruptName(listOf(classTestName("A"), classTestName("B")))
|
||||
|
||||
val carpenterSchema = obj.envelope.schema.mangleName(listOf(classTestName("A"), classTestName("B")))
|
||||
val metaCarpenter = TestMetaCarpenter(carpenterSchema.carpenterSchema())
|
||||
|
||||
assertEquals(1, metaCarpenter.schemas.carpenterSchemas.size)
|
||||
assertEquals(corruptName(classTestName("B")), metaCarpenter.schemas.carpenterSchemas.first().name)
|
||||
assertEquals(mangleName(classTestName("B")), metaCarpenter.schemas.carpenterSchemas.first().name)
|
||||
assertEquals(1, metaCarpenter.schemas.dependencies.size)
|
||||
assertTrue(corruptName(classTestName("A")) in metaCarpenter.schemas.dependencies)
|
||||
assertTrue(mangleName(classTestName("A")) in metaCarpenter.schemas.dependencies)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -8,15 +8,11 @@ import net.corda.core.serialization.amqp.*
|
||||
import org.junit.Test
|
||||
import kotlin.test.*
|
||||
|
||||
/*******************************************************************************************************/
|
||||
|
||||
@CordaSerializable
|
||||
interface J {
|
||||
val j: Int
|
||||
}
|
||||
|
||||
/*******************************************************************************************************/
|
||||
|
||||
@CordaSerializable
|
||||
interface I {
|
||||
val i: Int
|
||||
@ -39,49 +35,36 @@ interface IIII {
|
||||
val i: I
|
||||
}
|
||||
|
||||
/*******************************************************************************************************/
|
||||
|
||||
class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun interfaceParent1() {
|
||||
val testJ = 20
|
||||
|
||||
class A(override val j: Int) : J
|
||||
|
||||
val testJ = 20
|
||||
val a = A(testJ)
|
||||
|
||||
assertEquals(testJ, a.j)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
|
||||
val serSchema = obj.envelope.schema
|
||||
|
||||
assertEquals(2, serSchema.types.size)
|
||||
|
||||
val l1 = serSchema.carpenterSchema()
|
||||
|
||||
/* since we're using an envelope generated by seilaising classes defined locally
|
||||
it's extremely unlikely we'd need to carpent any classes */
|
||||
// since we're using an envelope generated by seilaising classes defined locally
|
||||
// it's extremely unlikely we'd need to carpent any classes
|
||||
assertEquals(0, l1.size)
|
||||
|
||||
val corruptSchema = serSchema.corruptName(listOf(classTestName("A")))
|
||||
|
||||
val l2 = corruptSchema.carpenterSchema()
|
||||
|
||||
val mangleSchema = serSchema.mangleName(listOf(classTestName("A")))
|
||||
val l2 = mangleSchema.carpenterSchema()
|
||||
assertEquals(1, l2.size)
|
||||
val aSchema = l2.carpenterSchemas.find { it.name == corruptName(classTestName("A")) }
|
||||
|
||||
val aSchema = l2.carpenterSchemas.find { it.name == mangleName(classTestName("A")) }
|
||||
assertNotEquals(null, aSchema)
|
||||
|
||||
assertEquals(corruptName(classTestName("A")), aSchema!!.name)
|
||||
assertEquals(mangleName(classTestName("A")), aSchema!!.name)
|
||||
assertEquals(1, aSchema.interfaces.size)
|
||||
assertEquals(net.corda.core.serialization.carpenter.J::class.java, aSchema.interfaces[0])
|
||||
|
||||
val aBuilder = ClassCarpenter().build(aSchema)
|
||||
|
||||
val objJ = aBuilder.constructors[0].newInstance(testJ)
|
||||
val j = objJ as J
|
||||
|
||||
@ -89,14 +72,12 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
assertEquals(a.j, j.j)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun interfaceParent2() {
|
||||
val testJ = 20
|
||||
val testJJ = 40
|
||||
|
||||
class A(override val j: Int, val jj: Int) : J
|
||||
|
||||
val testJ = 20
|
||||
val testJJ = 40
|
||||
val a = A(testJ, testJJ)
|
||||
|
||||
assertEquals(testJ, a.j)
|
||||
@ -112,14 +93,12 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
val l1 = serSchema.carpenterSchema()
|
||||
|
||||
/* since we're using an envelope generated by seilaising classes defined locally
|
||||
it's extremely unlikely we'd need to carpent any classes */
|
||||
assertEquals(0, l1.size)
|
||||
|
||||
val corruptSchema = serSchema.corruptName(listOf(classTestName("A")))
|
||||
val aName = corruptName(classTestName("A"))
|
||||
val mangleSchema = serSchema.mangleName(listOf(classTestName("A")))
|
||||
val aName = mangleName(classTestName("A"))
|
||||
|
||||
val l2 = corruptSchema.carpenterSchema()
|
||||
val l2 = mangleSchema.carpenterSchema()
|
||||
|
||||
assertEquals(1, l2.size)
|
||||
|
||||
@ -160,48 +139,43 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
val l1 = serSchema.carpenterSchema()
|
||||
|
||||
/* since we're using an envelope generated by seilaising classes defined locally
|
||||
it's extremely unlikely we'd need to carpent any classes */
|
||||
// since we're using an envelope generated by serialising classes defined locally
|
||||
// it's extremely unlikely we'd need to carpent any classes
|
||||
assertEquals(0, l1.size)
|
||||
|
||||
/* 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 corruptSchema = serSchema.corruptName(listOf(classTestName("A")))
|
||||
val l2 = corruptSchema.carpenterSchema()
|
||||
val aName = corruptName(classTestName("A"))
|
||||
// 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 mangleSchema = serSchema.mangleName(listOf(classTestName("A")))
|
||||
val l2 = mangleSchema.carpenterSchema()
|
||||
val aName = mangleName(classTestName("A"))
|
||||
|
||||
assertEquals(1, l2.size)
|
||||
|
||||
val aSchema = l2.carpenterSchemas.find { it.name == aName }
|
||||
|
||||
assertNotEquals(null, aSchema)
|
||||
|
||||
assertEquals(aName, aSchema!!.name)
|
||||
assertEquals(2, aSchema.interfaces.size)
|
||||
assert(net.corda.core.serialization.carpenter.I::class.java in aSchema.interfaces)
|
||||
assert(net.corda.core.serialization.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
|
||||
val ii = objA as II
|
||||
|
||||
assertEquals(aBuilder.getMethod("getI").invoke(objA), testI)
|
||||
assertEquals(aBuilder.getMethod("getIi").invoke(objA), testII)
|
||||
|
||||
assertEquals(a.i, i.i)
|
||||
assertEquals(a.ii, ii.ii)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nestedInterfaces() {
|
||||
val testI = 20
|
||||
val testIII = 60
|
||||
|
||||
class A(override val i: Int, override val iii: Int) : III
|
||||
|
||||
val testI = 20
|
||||
val testIII = 60
|
||||
val a = A(testI, testIII)
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
@ -213,34 +187,31 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
val l1 = serSchema.carpenterSchema()
|
||||
|
||||
/* since we're using an envelope generated by seilaising classes defined locally
|
||||
it's extremely unlikely we'd need to carpent any classes */
|
||||
// since we're using an envelope generated by serialising classes defined locally
|
||||
// it's extremely unlikely we'd need to carpent any classes
|
||||
assertEquals(0, l1.size)
|
||||
|
||||
val corruptSchema = serSchema.corruptName(listOf(classTestName("A")))
|
||||
val l2 = corruptSchema.carpenterSchema()
|
||||
val aName = corruptName(classTestName("A"))
|
||||
val mangleSchema = serSchema.mangleName(listOf(classTestName("A")))
|
||||
val l2 = mangleSchema.carpenterSchema()
|
||||
val aName = mangleName(classTestName("A"))
|
||||
|
||||
assertEquals(1, l2.size)
|
||||
|
||||
val aSchema = l2.carpenterSchemas.find { it.name == aName }
|
||||
|
||||
assertNotEquals(null, aSchema)
|
||||
|
||||
assertEquals(aName, aSchema!!.name)
|
||||
assertEquals(2, aSchema.interfaces.size)
|
||||
assert(net.corda.core.serialization.carpenter.I::class.java in aSchema.interfaces)
|
||||
assert(net.corda.core.serialization.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
|
||||
val iii = objA as III
|
||||
|
||||
assertEquals(aBuilder.getMethod("getI").invoke(objA), testI)
|
||||
assertEquals(aBuilder.getMethod("getIii").invoke(objA), testIII)
|
||||
|
||||
assertEquals(a.i, i.i)
|
||||
assertEquals(a.i, iii.i)
|
||||
assertEquals(a.iii, iii.iii)
|
||||
@ -248,33 +219,30 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun memberInterface() {
|
||||
val testI = 25
|
||||
val testIIII = 50
|
||||
|
||||
class A(override val i: Int) : I
|
||||
class B(override val i: I, override val iiii: Int) : IIII
|
||||
|
||||
val testI = 25
|
||||
val testIIII = 50
|
||||
val a = A(testI)
|
||||
val b = B(a, testIIII)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
|
||||
assert(obj.obj is B)
|
||||
|
||||
val serSchema = obj.envelope.schema
|
||||
|
||||
/*
|
||||
* class A
|
||||
* class A's interface (class I)
|
||||
* class B
|
||||
* class B's interface (class IIII)
|
||||
*/
|
||||
// Expected classes are
|
||||
// * class A
|
||||
// * class A's interface (class I)
|
||||
// * class B
|
||||
// * class B's interface (class IIII)
|
||||
assertEquals(4, serSchema.types.size)
|
||||
|
||||
val corruptSchema = serSchema.corruptName(listOf(classTestName("A"), classTestName("B")))
|
||||
val cSchema = corruptSchema.carpenterSchema()
|
||||
val aName = corruptName(classTestName("A"))
|
||||
val bName = corruptName(classTestName("B"))
|
||||
val mangleSchema = serSchema.mangleName(listOf(classTestName("A"), classTestName("B")))
|
||||
val cSchema = mangleSchema.carpenterSchema()
|
||||
val aName = mangleName(classTestName("A"))
|
||||
val bName = mangleName(classTestName("B"))
|
||||
|
||||
assertEquals(2, cSchema.size)
|
||||
|
||||
@ -286,102 +254,90 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
val cc = ClassCarpenter()
|
||||
val cc2 = ClassCarpenter()
|
||||
|
||||
val bBuilder = cc.build(bCarpenterSchema!!)
|
||||
bBuilder.constructors[0].newInstance(a, testIIII)
|
||||
|
||||
val 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 */
|
||||
// build a second B this time using our constructed instance of A and not the
|
||||
// local one we pre defined
|
||||
bBuilder.constructors[0].newInstance(objA, testIIII)
|
||||
|
||||
/* whittle and instantiate a different A with a new class loader */
|
||||
// whittle and instantiate a different A with a new class loader
|
||||
val aBuilder2 = cc2.build(aCarpenterSchema)
|
||||
val objA2 = aBuilder2.constructors[0].newInstance(testI)
|
||||
|
||||
bBuilder.constructors[0].newInstance(objA2, testIIII)
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
// if we remove the nested 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
|
||||
|
||||
class A(override val i: Int) : I
|
||||
class B(override val i: I, override val iiii: Int) : IIII
|
||||
|
||||
val testI = 25
|
||||
val testIIII = 50
|
||||
val a = A(testI)
|
||||
val b = B(a, testIIII)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
|
||||
|
||||
assert(obj.obj is B)
|
||||
|
||||
val serSchema = obj.envelope.schema
|
||||
|
||||
/*
|
||||
* The classes we're expecting to find:
|
||||
* class A
|
||||
* class A's interface (class I)
|
||||
* class B
|
||||
* class B's interface (class IIII)
|
||||
*/
|
||||
// The classes we're expecting to find:
|
||||
// * class A
|
||||
// * class A's interface (class I)
|
||||
// * class B
|
||||
// * class B's interface (class IIII)
|
||||
assertEquals(4, serSchema.types.size)
|
||||
|
||||
/* ignore the return as we expect this to throw */
|
||||
serSchema.corruptName(listOf(
|
||||
// ignore the return as we expect this to throw
|
||||
serSchema.mangleName(listOf(
|
||||
classTestName("A"), "${this.javaClass.`package`.name}.I")).carpenterSchema()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun interfaceAndImplementation() {
|
||||
val testI = 25
|
||||
|
||||
class A(override val i: Int) : I
|
||||
|
||||
val testI = 25
|
||||
val a = A(testI)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
|
||||
val serSchema = obj.envelope.schema
|
||||
|
||||
/*
|
||||
* The classes we're expecting to find:
|
||||
* class A
|
||||
* class A's interface (class I)
|
||||
*/
|
||||
// The classes we're expecting to find:
|
||||
// * class A
|
||||
// * class A's interface (class I)
|
||||
assertEquals(2, serSchema.types.size)
|
||||
|
||||
val amqpSchema = serSchema.corruptName(listOf(classTestName("A"), "${this.javaClass.`package`.name}.I"))
|
||||
|
||||
val aName = corruptName(classTestName("A"))
|
||||
val iName = corruptName("${this.javaClass.`package`.name}.I")
|
||||
|
||||
val amqpSchema = serSchema.mangleName(listOf(classTestName("A"), "${this.javaClass.`package`.name}.I"))
|
||||
val aName = mangleName(classTestName("A"))
|
||||
val iName = mangleName("${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 */
|
||||
// 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*/
|
||||
// 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 */
|
||||
// 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)
|
||||
@ -396,29 +352,26 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun twoInterfacesAndImplementation() {
|
||||
val testI = 69
|
||||
val testII = 96
|
||||
|
||||
class A(override val i: Int, override val ii: Int) : I, II
|
||||
|
||||
val testI = 69
|
||||
val testII = 96
|
||||
val a = A(testI, testII)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf(
|
||||
val amqpSchema = obj.envelope.schema.mangleName(listOf(
|
||||
classTestName("A"),
|
||||
"${this.javaClass.`package`.name}.I",
|
||||
"${this.javaClass.`package`.name}.II"))
|
||||
|
||||
val aName = corruptName(classTestName("A"))
|
||||
val iName = corruptName("${this.javaClass.`package`.name}.I")
|
||||
val iiName = corruptName("${this.javaClass.`package`.name}.II")
|
||||
|
||||
val aName = mangleName(classTestName("A"))
|
||||
val iName = mangleName("${this.javaClass.`package`.name}.I")
|
||||
val iiName = mangleName("${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 */
|
||||
// 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 })
|
||||
@ -438,8 +391,8 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
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)
|
||||
@ -451,58 +404,56 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun nestedInterfacesAndImplementation() {
|
||||
val testI = 7
|
||||
val testIII = 11
|
||||
|
||||
class A(override val i: Int, override val iii: Int) : III
|
||||
|
||||
val testI = 7
|
||||
val testIII = 11
|
||||
val a = A(testI, testIII)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
val amqpSchema = obj.envelope.schema.corruptName(listOf(
|
||||
val amqpSchema = obj.envelope.schema.mangleName(listOf(
|
||||
classTestName("A"),
|
||||
"${this.javaClass.`package`.name}.I",
|
||||
"${this.javaClass.`package`.name}.III"))
|
||||
|
||||
val aName = corruptName(classTestName("A"))
|
||||
val iName = corruptName("${this.javaClass.`package`.name}.I")
|
||||
val iiiName = corruptName("${this.javaClass.`package`.name}.III")
|
||||
val aName = mangleName(classTestName("A"))
|
||||
val iName = mangleName("${this.javaClass.`package`.name}.I")
|
||||
val iiiName = mangleName("${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) */
|
||||
// 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 */
|
||||
// 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 */
|
||||
// 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 */
|
||||
// conversly 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*/
|
||||
// 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)
|
||||
@ -512,5 +463,3 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
assert(iiiName in mc.objects)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.core.serialization.carpenter
|
||||
|
||||
import net.corda.core.serialization.carpenter.test.AmqpCarpenterBase
|
||||
import net.corda.core.serialization.carpenter.CarpenterSchemas
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.amqp.*
|
||||
|
||||
@ -13,14 +12,12 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun twoInts() {
|
||||
val testA = 10
|
||||
val testB = 20
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Int, val b: Int)
|
||||
|
||||
var a = A(testA, testB)
|
||||
|
||||
val testA = 10
|
||||
val testB = 20
|
||||
val a = A(testA, testB)
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
@ -31,7 +28,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
assertEquals(1, obj.envelope.schema.types.size)
|
||||
assert(obj.envelope.schema.types[0] is CompositeType)
|
||||
|
||||
var amqpSchema = obj.envelope.schema.types[0] as CompositeType
|
||||
val amqpSchema = obj.envelope.schema.types[0] as CompositeType
|
||||
|
||||
assertEquals(2, amqpSchema.fields.size)
|
||||
assertEquals("a", amqpSchema.fields[0].name)
|
||||
@ -48,7 +45,6 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
assertNotEquals(null, aSchema)
|
||||
|
||||
val pinochio = ClassCarpenter().build(aSchema!!)
|
||||
|
||||
val p = pinochio.constructors[0].newInstance(testA, testB)
|
||||
|
||||
assertEquals(pinochio.getMethod("getA").invoke(p), amqpObj.a)
|
||||
@ -57,14 +53,12 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun intAndStr() {
|
||||
val testA = 10
|
||||
val testB = "twenty"
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Int, val b: String)
|
||||
|
||||
val testA = 10
|
||||
val testB = "twenty"
|
||||
val a = A(testA, testB)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
@ -92,7 +86,6 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
assertNotEquals(null, aSchema)
|
||||
|
||||
val pinochio = ClassCarpenter().build(aSchema!!)
|
||||
|
||||
val p = pinochio.constructors[0].newInstance(testA, testB)
|
||||
|
||||
assertEquals(pinochio.getMethod("getA").invoke(p), amqpObj.a)
|
||||
|
@ -1,23 +1,19 @@
|
||||
package net.corda.core.serialization.carpenter
|
||||
|
||||
import net.corda.core.serialization.carpenter.test.AmqpCarpenterBase
|
||||
import net.corda.core.serialization.carpenter.CarpenterSchemas
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.amqp.*
|
||||
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun singleInteger() {
|
||||
val test = 10
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Int)
|
||||
|
||||
val test = 10
|
||||
val a = A(test)
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
@ -39,7 +35,6 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName("A") }!!
|
||||
val aBuilder = ClassCarpenter().build(aSchema)
|
||||
|
||||
val p = aBuilder.constructors[0].newInstance(test)
|
||||
|
||||
assertEquals(aBuilder.getMethod("getA").invoke(p), amqpObj.a)
|
||||
@ -47,11 +42,10 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun singleString() {
|
||||
val test = "ten"
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: String)
|
||||
|
||||
val test = "ten"
|
||||
val a = A(test)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
@ -64,13 +58,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
assert(obj.envelope.schema.types[0] is CompositeType)
|
||||
|
||||
val amqpSchema = obj.envelope.schema.types[0] as CompositeType
|
||||
|
||||
val carpenterSchema = CarpenterSchemas.newInstance()
|
||||
amqpSchema.carpenterSchema(carpenterSchemas = carpenterSchema, force = true)
|
||||
|
||||
val aSchema = carpenterSchema.carpenterSchemas.find { it.name == classTestName("A") }!!
|
||||
val aBuilder = ClassCarpenter().build(aSchema)
|
||||
|
||||
val p = aBuilder.constructors[0].newInstance(test)
|
||||
|
||||
assertEquals(aBuilder.getMethod("getA").invoke(p), amqpObj.a)
|
||||
@ -78,13 +70,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun singleLong() {
|
||||
val test = 10L
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Long)
|
||||
|
||||
val test = 10L
|
||||
val a = A(test)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
@ -112,13 +102,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun singleShort() {
|
||||
val test = 10.toShort()
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Short)
|
||||
|
||||
val test = 10.toShort()
|
||||
val a = A(test)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
@ -146,13 +134,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun singleDouble() {
|
||||
val test = 10.0
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Double)
|
||||
|
||||
val test = 10.0
|
||||
val a = A(test)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
@ -180,13 +166,11 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() {
|
||||
|
||||
@Test
|
||||
fun singleFloat() {
|
||||
val test: Float = 10.0F
|
||||
|
||||
@CordaSerializable
|
||||
data class A(val a: Float)
|
||||
|
||||
val test: Float = 10.0F
|
||||
val a = A(test)
|
||||
|
||||
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
|
||||
|
||||
assert(obj.obj is A)
|
||||
|
Loading…
Reference in New Issue
Block a user