diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt index 5e5186c343..e0a54e8a8a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt @@ -47,8 +47,9 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme { } fun getSerializerFactory(): SerializerFactory { - return serializerFactoriesForContexts.computeIfAbsent(Pair(AllWhitelist, deserializationClassLoader)) { - SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + return serializerFactoriesForContexts.computeIfAbsent(Pair( + AllWhitelist, SerializationDefaults.javaClass.classLoader)) { + SerializerFactory(AllWhitelist, SerializationDefaults.javaClass.classLoader) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoAMQPSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoAMQPSerializer.kt index dae649b285..90ea983388 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoAMQPSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoAMQPSerializer.kt @@ -6,13 +6,9 @@ import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationFactory -import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.sequence import net.corda.nodeapi.internal.serialization.amqp.AmqpHeaderV1_0 import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput -import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput -import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import net.corda.nodeapi.internal.serialization.amqp.SerializerFactoryFactory /** * This [Kryo] custom [Serializer] switches the object graph of anything annotated with `@CordaSerializable` diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt index 51911f2d24..8f602a65de 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt @@ -1,5 +1,6 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationDefaults import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -68,26 +69,26 @@ abstract class CustomSerializer : AMQPSerializer { } /** - * Additional base features for a custom serializer for a particular class, that excludes subclasses. + * Additional base features for a custom serializer for a particular class [withInheritance] is false + * or super class / interfaces [withInheritance] is true */ - abstract class Is(protected val clazz: Class) : CustomSerializer() { - override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == this.clazz + abstract class CustomSerializerImp(protected val clazz: Class, protected val withInheritance: Boolean) : CustomSerializer() { override val type: Type get() = clazz override val typeDescriptor: String = "$DESCRIPTOR_DOMAIN:${nameForType(clazz)}" override fun writeClassInfo(output: SerializationOutput) {} override val descriptor: Descriptor = Descriptor(typeDescriptor) + override fun isSerializerFor(clazz: Class<*>): Boolean = if (withInheritance) this.clazz.isAssignableFrom(clazz) else this.clazz == clazz } + /** + * Additional base features for a custom serializer for a particular class, that excludes subclasses. + */ + abstract class Is(clazz: Class) : CustomSerializerImp(clazz, false) + /** * Additional base features for a custom serializer for all implementations of a particular interface or super class. */ - abstract class Implements(protected val clazz: Class) : CustomSerializer() { - override fun isSerializerFor(clazz: Class<*>): Boolean = this.clazz.isAssignableFrom(clazz) - override val type: Type get() = clazz - override val typeDescriptor: String = "$DESCRIPTOR_DOMAIN:${nameForType(clazz)}" - override fun writeClassInfo(output: SerializationOutput) {} - override val descriptor: Descriptor = Descriptor(typeDescriptor) - } + abstract class Implements(clazz: Class) : CustomSerializerImp(clazz, true) /** * Additional base features over and above [Implements] or [Is] custom serializer for when the serialized form should be @@ -96,15 +97,11 @@ abstract class CustomSerializer : AMQPSerializer { * The proxy class must use only types which are either native AMQP or other types for which there are pre-registered * custom serializers. */ - abstract class Proxy(protected val clazz: Class, + abstract class Proxy(clazz: Class, protected val proxyClass: Class

, protected val factory: SerializerFactory, - val withInheritance: Boolean = true) : CustomSerializer() { + withInheritance: Boolean = true) : CustomSerializerImp(clazz, withInheritance) { override fun isSerializerFor(clazz: Class<*>): Boolean = if (withInheritance) this.clazz.isAssignableFrom(clazz) else this.clazz == clazz - override val type: Type get() = clazz - override val typeDescriptor: String = "$DESCRIPTOR_DOMAIN:${nameForType(clazz)}" - override fun writeClassInfo(output: SerializationOutput) {} - override val descriptor: Descriptor = Descriptor(typeDescriptor) private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyClass, factory) } @@ -152,26 +149,26 @@ abstract class CustomSerializer : AMQPSerializer { */ abstract class ToString(clazz: Class, withInheritance: Boolean = false, private val maker: (String) -> T = clazz.getConstructor(String::class.java).let { - `constructor` -> { string -> `constructor`.newInstance(string) } + `constructor` -> + { string -> `constructor`.newInstance(string) } }, - private val unmaker: (T) -> String = { obj -> obj.toString() }) : Proxy(clazz, String::class.java, /* Unused */ SerializerFactoryFactory.get(), withInheritance) { + private val unmaker: (T) -> String = { obj -> obj.toString() }) + : CustomSerializerImp(clazz, withInheritance) { override val additionalSerializers: Iterable> = emptyList() - override val schemaForDocumentation = Schema(listOf(RestrictedType(nameForType(type), "", listOf(nameForType(type)), SerializerFactory.primitiveTypeName(String::class.java)!!, descriptor, emptyList()))) - - override fun toProxy(obj: T): String = unmaker(obj) - - override fun fromProxy(proxy: String): T = maker(proxy) + override val schemaForDocumentation = Schema( + listOf(RestrictedType(nameForType(type), "", listOf(nameForType(type)), + SerializerFactory.primitiveTypeName(String::class.java)!!, + descriptor, emptyList()))) override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) { - val proxy = toProxy(obj) - data.putObject(proxy) + data.putObject(unmaker(obj)) } override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): T { val proxy = input.readObject(obj, schema, String::class.java) as String - return fromProxy(proxy) + return maker(proxy) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt index 8764064116..621b01d509 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt @@ -20,7 +20,7 @@ data class ObjectAndEnvelope(val obj: T, val envelope: Envelope) * @param serializerFactory This is the factory for [AMQPSerializer] instances and can be shared across multiple * instances and threads. */ -class DeserializationInput(internal val serializerFactory: SerializerFactory = SerializerFactoryFactory.get()) { +class DeserializationInput(internal val serializerFactory: SerializerFactory) { // TODO: we're not supporting object refs yet private val objectHistory: MutableList = ArrayList() diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt index 7e47932f06..39b96b9679 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt @@ -14,7 +14,7 @@ import kotlin.collections.LinkedHashSet * @param serializerFactory This is the factory for [AMQPSerializer] instances and can be shared across multiple * instances and threads. */ -open class SerializationOutput(internal val serializerFactory: SerializerFactory = SerializerFactoryFactory.get()) { +open class SerializationOutput(internal val serializerFactory: SerializerFactory) { // TODO: we're not supporting object refs yet private val objectHistory: MutableMap = IdentityHashMap() private val serializerHistory: MutableSet> = LinkedHashSet() diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactoryFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactoryFactory.kt deleted file mode 100644 index 81a2643918..0000000000 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactoryFactory.kt +++ /dev/null @@ -1,22 +0,0 @@ -package net.corda.nodeapi.internal.serialization.amqp - -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.ClassWhitelist -import net.corda.nodeapi.internal.serialization.AllWhitelist - -/** - * Factory singleton that maps unique Serializer Factories from a pair of WhitleList and ClassLoader - */ -object SerializerFactoryFactory { - val factories : MutableMap, SerializerFactory> = mutableMapOf() - - fun get(context: SerializationContext) : SerializerFactory = - factories.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) { - SerializerFactory(context.whitelist, context.deserializationClassLoader) - } - - fun get() : SerializerFactory = - factories.computeIfAbsent(Pair(AllWhitelist, ClassLoader.getSystemClassLoader())) { - SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) - } -} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt index c22a70f4d9..e1f7db12c8 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt @@ -1,10 +1,14 @@ package net.corda.nodeapi.internal.serialization.amqp import org.apache.qpid.proton.codec.Data +import net.corda.nodeapi.internal.serialization.AllWhitelist + +fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) class TestSerializationOutput( private val verbose: Boolean, - serializerFactory: SerializerFactory = SerializerFactoryFactory.get()) : SerializationOutput(serializerFactory) { + serializerFactory: SerializerFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())) + : SerializationOutput(serializerFactory) { override fun writeSchema(schema: Schema, data: Data) { if (verbose) println(schema) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt index 39ed38bea4..6747b15db9 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt @@ -18,7 +18,7 @@ class DeserializeAndReturnEnvelopeTests { val a = A(10, "20") - val factory = SerializerFactoryFactory.get() + val factory = testDefaultFactory() fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a)) @@ -34,7 +34,7 @@ class DeserializeAndReturnEnvelopeTests { val b = B(A(10, "20"), 30.0F) - val factory = SerializerFactoryFactory.get() + val factory = testDefaultFactory() fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b)) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt index 1e7171b31a..88ec97ca42 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt @@ -16,30 +16,30 @@ class DeserializeSimpleTypesTests { private const val VERBOSE = false } - val sf1 = SerializerFactory() - val sf2 = SerializerFactory() + val sf1 = testDefaultFactory() + val sf2 = testDefaultFactory() @Test fun testChar() { data class C(val c: Char) - var deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('c'))) + var deserializedC = DeserializationInput(sf).deserialize(SerializationOutput(sf).serialize(C('c'))) assertEquals('c', deserializedC.c) // CYRILLIC CAPITAL LETTER YU (U+042E) - deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('Ю'))) + deserializedC = DeserializationInput(sf).deserialize(SerializationOutput(sf).serialize(C('Ю'))) assertEquals('Ю', deserializedC.c) // ARABIC LETTER FEH WITH DOT BELOW (U+06A3) - deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('ڣ'))) + deserializedC = DeserializationInput(sf).deserialize(SerializationOutput(sf).serialize(C('ڣ'))) assertEquals('ڣ', deserializedC.c) // ARABIC LETTER DAD WITH DOT BELOW (U+06FB) - deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('ۻ'))) + deserializedC = DeserializationInput(sf).deserialize(SerializationOutput(sf).serialize(C('ۻ'))) assertEquals('ۻ', deserializedC.c) // BENGALI LETTER AA (U+0986) - deserializedC = DeserializationInput().deserialize(SerializationOutput().serialize(C('আ'))) + deserializedC = DeserializationInput(sf).deserialize(SerializationOutput(sf).serialize(C('আ'))) assertEquals('আ', deserializedC.c) } @@ -49,8 +49,8 @@ class DeserializeSimpleTypesTests { data class C(val c: Character) val c = C(Character('c')) - val serialisedC = SerializationOutput().serialize(c) - val deserializedC = DeserializationInput().deserialize(serialisedC) + val serialisedC = SerializationOutput(sf).serialize(c) + val deserializedC = DeserializationInput(sf).deserialize(serialisedC) assertEquals(c.c, deserializedC.c) } @@ -60,8 +60,8 @@ class DeserializeSimpleTypesTests { data class C(val c: Char?) val c = C(null) - val serialisedC = SerializationOutput().serialize(c) - val deserializedC = DeserializationInput().deserialize(serialisedC) + val serialisedC = SerializationOutput(sf).serialize(c) + val deserializedC = DeserializationInput(sf).deserialize(serialisedC) assertEquals(c.c, deserializedC.c) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 3f4e534004..70a68e8cce 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -14,6 +14,9 @@ import net.corda.nodeapi.RPCException import net.corda.nodeapi.internal.serialization.AbstractAMQPSerializationScheme import net.corda.nodeapi.internal.serialization.EmptyWhitelist import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive +import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl +import net.corda.nodeapi.internal.serialization.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_PUBKEY import org.apache.qpid.proton.amqp.* @@ -135,7 +138,8 @@ class SerializationOutputTests { data class PolymorphicProperty(val foo: FooInterface?) private fun serdes(obj: Any, - factory: SerializerFactory = SerializerFactoryFactory.get(), + factory: SerializerFactory = SerializerFactory ( + AllWhitelist, ClassLoader.getSystemClassLoader()), freshDeserializationFactory: SerializerFactory = SerializerFactory( AllWhitelist, ClassLoader.getSystemClassLoader()), expectedEqual: Boolean = true, @@ -527,10 +531,10 @@ class SerializationOutputTests { fun `test transaction state`() { val state = TransactionState(FooState(), MEGA_CORP) - val factory = SerializerFactory() + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) AbstractAMQPSerializationScheme.registerCustomSerializers(factory) - val factory2 = SerializerFactory() + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) AbstractAMQPSerializationScheme.registerCustomSerializers(factory2) val desState = serdes(state, factory, factory2, expectedEqual = false, expectDeserializedEqual = false) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt index 2dd62bff58..b7f1c2d348 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt @@ -1,11 +1,8 @@ package net.corda.nodeapi.internal.serialization.carpenter +import net.corda.nodeapi.internal.serialization.amqp.* import net.corda.nodeapi.internal.serialization.amqp.Field import net.corda.nodeapi.internal.serialization.amqp.Schema -import net.corda.nodeapi.internal.serialization.amqp.TypeNotation -import net.corda.nodeapi.internal.serialization.amqp.CompositeType -import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput -import net.corda.nodeapi.internal.serialization.amqp.SerializerFactoryFactory fun mangleName(name: String) = "${name}__carpenter" @@ -37,7 +34,7 @@ fun Schema.mangleNames(names: List): Schema { } open class AmqpCarpenterBase { - var factory = SerializerFactoryFactory.get() + var factory = testDefaultFactory() fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) fun testName(): String = Thread.currentThread().stackTrace[2].methodName