CORDA-2688 - Add Serialization Context option for no carpenting (#4849)

* CORDA-2688 - Add Serialization Context option for no carpenting

Can be used by the attachment class loader - Serialization Framework
will still consume all Exceptions and throw a NotSerializableException

* Fix tests
This commit is contained in:
Katelyn Baker
2019-03-04 17:44:56 +00:00
committed by GitHub
parent 0984e87c5e
commit cfccfd075e
16 changed files with 122 additions and 57 deletions

View File

@ -7,6 +7,7 @@ import net.corda.core.serialization.SerializedBytes;
import net.corda.serialization.internal.AllWhitelist;
import net.corda.serialization.internal.amqp.*;
import net.corda.serialization.internal.amqp.Schema;
import net.corda.serialization.internal.amqp.testutils.TestSerializationContext;
import net.corda.serialization.internal.model.RemoteTypeInformation;
import net.corda.serialization.internal.model.TypeIdentifier;
import net.corda.testing.core.SerializationEnvironmentRule;
@ -77,7 +78,7 @@ public class JavaCalculatedValuesToClassCarpenterTest extends AmqpCarpenterBase
RemoteTypeInformation renamed = rename(typeInformation, typeToMangle, mangle(typeToMangle));
Class<?> pinochio = load(renamed);
Class<?> pinochio = load(renamed, TestSerializationContext.testSerializationContext);
Object p = pinochio.getConstructors()[0].newInstance(4, 2, "4");
assertEquals(2, pinochio.getMethod("getI").invoke(p));

View File

@ -1,11 +1,15 @@
package net.corda.serialization.internal.amqp
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.testutils.*
import net.corda.serialization.internal.carpenter.*
import org.junit.Test
import java.io.NotSerializableException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
@ -35,6 +39,17 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
// Deserialize with whitelisting on to check that `CordaSerializable` annotation present.
private val sf2 = testDefaultFactoryWithWhitelist()
private inline fun <reified T : Any> DeserializationInput.deserializeWithoutAndWithCarpenter(
bytes: SerializedBytes<T>,
context: SerializationContext? = null
) : T {
assertFailsWith(NotSerializableException::class) {
deserialize(bytes, T::class.java, (context ?: testSerializationContext).withoutCarpenter())
}
return deserialize(bytes, T::class.java, context ?: testSerializationContext)
}
@Test
fun verySimpleType() {
val testVal = 10
@ -52,19 +67,20 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
assertEquals(deserializedObj1::class.java, deserializedObj2::class.java)
assertEquals(testVal, deserializedObj2::class.java.getMethod("getA").invoke(deserializedObj2))
val deserializedObj3 = DeserializationInput(sf2).deserialize(serialisedBytes)
val deserializedObj3 = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes)
assertNotEquals(clazz, deserializedObj3::class.java)
assertNotEquals(deserializedObj1::class.java, deserializedObj3::class.java)
assertNotEquals(deserializedObj2::class.java, deserializedObj3::class.java)
assertEquals(testVal, deserializedObj3::class.java.getMethod("getA").invoke(deserializedObj3))
// NOTE: There is no point attempting this without the carepenter a second time as having carpented things up once
// it will, of course, just succeed even with the carpenter disabled
val deserializedObj4 = DeserializationInput(sf2).deserialize(serialisedBytes)
assertNotEquals(clazz, deserializedObj4::class.java)
assertNotEquals(deserializedObj1::class.java, deserializedObj4::class.java)
assertNotEquals(deserializedObj2::class.java, deserializedObj4::class.java)
assertEquals(deserializedObj3::class.java, deserializedObj4::class.java)
assertEquals(testVal, deserializedObj4::class.java.getMethod("getA").invoke(deserializedObj4))
}
@Test
@ -79,7 +95,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
val concreteB = clazz.constructors[0].newInstance(testValB)
val concreteC = clazz.constructors[0].newInstance(testValC)
val deserialisedA = DeserializationInput(sf2).deserialize(
val deserialisedA = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(
TestSerializationOutput(VERBOSE, sf1).serialize(concreteA))
assertEquals(testValA, deserialisedA::class.java.getMethod("getA").invoke(deserialisedA))
@ -114,7 +130,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
val classInstance = clazz.constructors[0].newInstance(testVal)
val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(classInstance)
val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes)
val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes)
assertTrue(deserializedObj is I)
assertEquals(testVal, (deserializedObj as I).getName())
@ -133,7 +149,8 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
clazz.constructors[0].newInstance(2),
clazz.constructors[0].newInstance(3)))
val deserializedObj = DeserializationInput(sf2).deserialize(TestSerializationOutput(VERBOSE, sf1).serialize(outer))
val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(
TestSerializationOutput(VERBOSE, sf1).serialize(outer))
assertNotEquals((deserializedObj.a[0])::class.java, (outer.a[0])::class.java)
assertNotEquals((deserializedObj.a[1])::class.java, (outer.a[1])::class.java)
@ -164,9 +181,9 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
val outer = outerType.constructors[0].newInstance(innerType.constructors[0].newInstance(2))
val serializedI = TestSerializationOutput(VERBOSE, sf1).serialize(inner)
val deserialisedI = DeserializationInput(sf2).deserialize(serializedI)
val deserialisedI = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serializedI)
val serialisedO = TestSerializationOutput(VERBOSE, sf1).serialize(outer)
val deserialisedO = DeserializationInput(sf2).deserialize(serialisedO)
val deserialisedO = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedO)
// ensure out carpented version of inner is reused
assertEquals(deserialisedI::class.java,
@ -184,7 +201,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
val classInstance = outerClass.constructors.first().newInstance(nestedClass.constructors.first().newInstance("name"))
val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(classInstance)
val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes)
val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes)
val inner = deserializedObj::class.java.getMethod("getInner").invoke(deserializedObj)
assertEquals("name", inner::class.java.getMethod("getName").invoke(inner))
@ -204,7 +221,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
nestedClass.constructors.first().newInstance("bar"))
val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(classInstance)
val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes)
val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes)
assertEquals("foo", deserializedObj.a::class.java.getMethod("getName").invoke(deserializedObj.a))
assertEquals("bar", deserializedObj.b::class.java.getMethod("getName").invoke(deserializedObj.b))
@ -226,7 +243,8 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
unknownClass.constructors.first().newInstance(7, 8)))
val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(toSerialise)
val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes)
val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes)
var sentinel = 1
deserializedObj.l.forEach {
assertEquals(sentinel++, it::class.java.getMethod("getV1").invoke(it))
@ -249,8 +267,8 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) {
val serialisedBytes = TestSerializationOutput(VERBOSE, sf1).serialize(
concreteClass.constructors.first().newInstance(12, "timmy"))
val deserializedObj = DeserializationInput(sf2).deserialize(serialisedBytes)
val deserializedObj = DeserializationInput(sf2).deserializeWithoutAndWithCarpenter(serialisedBytes)
assertTrue(deserializedObj is I)
assertEquals("timmy", (deserializedObj as I).getName())
assertEquals("timmy", deserializedObj::class.java.getMethod("getName").invoke(deserializedObj))

View File

@ -550,7 +550,7 @@ class DeserializeSimpleTypesTests {
@Test
fun classHasNoPublicConstructor() {
assertFailsWithMessage("Trying to build an object serializer for ${Garbo::class.java.name}, " +
"but it is not constructible from its public properties, and so requires a custom serialiser.") {
"but it is not constructable from its public properties, and so requires a custom serialiser.") {
TestSerializationOutput(VERBOSE, sf1).serializeAndReturnSchema(Garbo.make(1))
}
}
@ -558,7 +558,7 @@ class DeserializeSimpleTypesTests {
@Test
fun propertyClassHasNoPublicConstructor() {
assertFailsWithMessage("Trying to build an object serializer for ${Greta::class.java.name}, " +
"but it is not constructible from its public properties, and so requires a custom serialiser.") {
"but it is not constructable from its public properties, and so requires a custom serialiser.") {
TestSerializationOutput(VERBOSE, sf1).serializeAndReturnSchema(Greta(Garbo.make(1)))
}
}

View File

@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter
import com.google.common.reflect.TypeToken
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.serialization.internal.amqp.*
import net.corda.serialization.internal.amqp.testutils.deserializeAndReturnEnvelope
@ -83,11 +84,11 @@ open class AmqpCarpenterBase(whitelist: ClassWhitelist) {
else -> this
}
protected fun RemoteTypeInformation.load(): Class<*> =
typeLoader.load(listOf(this))[typeIdentifier]!!.asClass()
protected fun RemoteTypeInformation.load(context : SerializationContext): Class<*> =
typeLoader.load(listOf(this), context)[typeIdentifier]!!.asClass()
protected fun assertCanLoadAll(vararg types: RemoteTypeInformation) {
assertTrue(typeLoader.load(types.asList()).keys.containsAll(types.map { it.typeIdentifier }))
protected fun assertCanLoadAll(context: SerializationContext, vararg types: RemoteTypeInformation) {
assertTrue(typeLoader.load(types.asList(), context).keys.containsAll(types.map { it.typeIdentifier }))
}
protected fun Class<*>.new(vararg constructorParams: Any?) =

View File

@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.testutils.testSerializationContext
import org.junit.Test
import java.io.NotSerializableException
import java.util.*
@ -26,7 +27,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val (_, envelope) = B(A(10), 20).roundTrip()
// We load an unknown class, B_mangled, which includes a reference to a known class, A.
assertCanLoadAll(envelope.getMangled<B>())
assertCanLoadAll(testSerializationContext, envelope.getMangled<B>())
}
@Test
@ -41,7 +42,8 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
// We load an unknown class, B_mangled, which includes a reference to an unknown class, A_mangled.
// For this to work, we must include A_mangled in our set of classes to load.
assertCanLoadAll(envelope.getMangled<B>().mangle<A>(), envelope.getMangled<A>())
assertCanLoadAll(testSerializationContext,
envelope.getMangled<B>().mangle<A>(), envelope.getMangled<A>())
}
@Test
@ -56,7 +58,8 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
// We load an unknown class, B_mangled, which includes a reference to an unknown class, A_mangled.
// This will fail, because A_mangled is not included in our set of classes to load.
assertFailsWith<NotSerializableException> { assertCanLoadAll(envelope.getMangled<B>().mangle<A>()) }
assertFailsWith<NotSerializableException> { assertCanLoadAll(testSerializationContext,
envelope.getMangled<B>().mangle<A>()) }
}
// See https://github.com/corda/corda/issues/4107
@ -71,7 +74,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val uuid = UUID.randomUUID()
val(_, envelope) = IOUStateData(10, uuid, "new value").roundTrip()
val recarpented = envelope.getMangled<IOUStateData>().load()
val recarpented = envelope.getMangled<IOUStateData>().load(testSerializationContext)
val instance = recarpented.new(null, uuid, 10)
assertEquals(uuid, instance.get("ref"))
}
@ -90,7 +93,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
"java.util.Map<java.lang.String, ${mangledC.typeIdentifier.prettyPrint(false)}>",
mangledMap.prettyPrint(false))
assertCanLoadAll(infoForD, mangledMap, mangledC)
assertCanLoadAll(testSerializationContext, infoForD, mangledMap, mangledC)
}
@Test
@ -104,6 +107,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val mangledNotAMap = envelope.typeInformationFor<NotAMap<String, C>>().mangle<C>()
val mangledC = envelope.getMangled<C>()
assertCanLoadAll(infoForD, mangledNotAMap, mangledC)
assertCanLoadAll(testSerializationContext, infoForD, mangledNotAMap, mangledC)
}
}

View File

@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.testutils.testSerializationContext
import org.junit.Test
import kotlin.test.*
import java.io.NotSerializableException
@ -41,7 +42,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val (_, env) = A(20).roundTrip()
val mangledA = env.getMangled<A>()
val carpentedA = mangledA.load()
val carpentedA = mangledA.load(testSerializationContext)
val carpentedInstance = carpentedA.new(20)
assertEquals(20, carpentedInstance.get("j"))
@ -52,10 +53,11 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
@Test
fun interfaceParent2() {
@Suppress("UNUSED")
class A(override val j: Int, val jj: Int) : J
val (_, env) = A(23, 42).roundTrip()
val carpentedA = env.getMangled<A>().load()
val carpentedA = env.getMangled<A>().load(testSerializationContext)
val carpetedInstance = carpentedA.constructors[0].newInstance(23, 42)
assertEquals(23, carpetedInstance.get("j"))
@ -70,7 +72,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
class A(override val i: Int, override val ii: Int) : I, II
val (_, env) = A(23, 42).roundTrip()
val carpentedA = env.getMangled<A>().load()
val carpentedA = env.getMangled<A>().load(testSerializationContext)
val carpetedInstance = carpentedA.constructors[0].newInstance(23, 42)
assertEquals(23, carpetedInstance.get("i"))
@ -88,7 +90,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
class A(override val i: Int, override val iii: Int) : III
val (_, env) = A(23, 42).roundTrip()
val carpentedA = env.getMangled<A>().load()
val carpentedA = env.getMangled<A>().load(testSerializationContext)
val carpetedInstance = carpentedA.constructors[0].newInstance(23, 42)
assertEquals(23, carpetedInstance.get("i"))
@ -108,8 +110,8 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
class B(override val i: I, override val iiii: Int) : IIII
val (_, env) = B(A(23), 42).roundTrip()
val carpentedA = env.getMangled<A>().load()
val carpentedB = env.getMangled<B>().load()
val carpentedA = env.getMangled<A>().load(testSerializationContext)
val carpentedB = env.getMangled<B>().load(testSerializationContext)
val carpentedAInstance = carpentedA.new(23)
val carpentedBInstance = carpentedB.new(carpentedAInstance, 42)
@ -127,7 +129,9 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
// 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
assertFailsWith<NotSerializableException> { assertCanLoadAll(env.getMangled<A>().mangle<I>()) }
assertFailsWith<NotSerializableException> { assertCanLoadAll(
testSerializationContext,
env.getMangled<A>().mangle<I>()) }
}
@Test
@ -137,7 +141,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val (_, env) = A(23).roundTrip()
// This time around we will succeed, because the mangled I is included in the type information to be loaded.
assertCanLoadAll(env.getMangled<A>().mangle<I>(), env.getMangled<I>())
assertCanLoadAll(testSerializationContext, env.getMangled<A>().mangle<I>(), env.getMangled<I>())
}
@Test
@ -146,6 +150,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val (_, env) = A(23, 42).roundTrip()
assertCanLoadAll(
testSerializationContext,
env.getMangled<A>().mangle<I>().mangle<II>(),
env.getMangled<I>(),
env.getMangled<II>()
@ -158,6 +163,7 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val (_, env) = A(23, 42).roundTrip()
assertCanLoadAll(
testSerializationContext,
env.getMangled<A>().mangle<I>().mangle<III>(),
env.getMangled<I>(),
env.getMangled<III>().mangle<I>()

View File

@ -3,6 +3,7 @@ package net.corda.serialization.internal.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.testutils.testSerializationContext
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
@ -15,7 +16,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
data class A(val a: Int, val b: Long)
val (_, env) = A(23, 42).roundTrip()
val carpentedInstance = env.getMangled<A>().load().new(23, 42)
val carpentedInstance = env.getMangled<A>().load(testSerializationContext).new(23, 42)
assertEquals(23, carpentedInstance.get("a"))
assertEquals(42L, carpentedInstance.get("b"))
@ -27,7 +28,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
data class A(val a: Int, val b: String)
val (_, env) = A(23, "skidoo").roundTrip()
val carpentedInstance = env.getMangled<A>().load().new(23, "skidoo")
val carpentedInstance = env.getMangled<A>().load(testSerializationContext).new(23, "skidoo")
assertEquals(23, carpentedInstance.get("a"))
assertEquals("skidoo", carpentedInstance.get("b"))
@ -57,7 +58,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
squared: String
""".trimIndent(), remoteTypeInformation.prettyPrint())
val pinochio = remoteTypeInformation.mangle<C>().load()
val pinochio = remoteTypeInformation.mangle<C>().load(testSerializationContext)
assertNotEquals(pinochio.name, C::class.java.name)
assertNotEquals(pinochio, C::class.java)
@ -78,7 +79,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
val (_, env) = C(5).roundTrip()
val pinochio = env.getMangled<C>().load()
val pinochio = env.getMangled<C>().load(testSerializationContext)
val p = pinochio.new(5)
assertEquals(5, p.get("doubled"))

View File

@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.reflect.TypeToken
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.asClass
import net.corda.serialization.internal.amqp.testutils.testSerializationContext
import net.corda.serialization.internal.carpenter.ClassCarpenterImpl
import org.junit.Test
import java.lang.reflect.Type
@ -12,8 +13,8 @@ import kotlin.test.assertEquals
class ClassCarpentingTypeLoaderTests {
val carpenter = ClassCarpenterImpl(AllWhitelist)
val remoteTypeCarpenter = SchemaBuildingRemoteTypeCarpenter(carpenter)
val typeLoader = ClassCarpentingTypeLoader(remoteTypeCarpenter, carpenter.classloader)
private val remoteTypeCarpenter = SchemaBuildingRemoteTypeCarpenter(carpenter)
private val typeLoader = ClassCarpentingTypeLoader(remoteTypeCarpenter, carpenter.classloader)
@Test
fun `carpent some related classes`() {
@ -44,7 +45,9 @@ class ClassCarpentingTypeLoaderTests {
"previousAddresses" to listOfAddresses.mandatory
), emptyList(), emptyList())
val types = typeLoader.load(listOf(personInformation, addressInformation, listOfAddresses))
val types = typeLoader.load(listOf(personInformation, addressInformation, listOfAddresses),
testSerializationContext)
val addressType = types[addressInformation.typeIdentifier]!!
val personType = types[personInformation.typeIdentifier]!!