diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt index eb9f22e937..5a6adc58b9 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/KryoClientSerializationScheme.kt @@ -41,7 +41,7 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() { return SerializationEnvironmentImpl( SerializationFactoryImpl().apply { registerScheme(KryoClientSerializationScheme()) - registerScheme(AMQPClientSerializationScheme()) + registerScheme(AMQPClientSerializationScheme(emptyList())) }, KRYO_P2P_CONTEXT, rpcClientContext = KRYO_RPC_CLIENT_CONTEXT) diff --git a/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt b/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt index f4fe71ead0..d4df6eb5ec 100644 --- a/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt +++ b/core/src/main/kotlin/net/corda/core/cordapp/Cordapp.kt @@ -3,6 +3,7 @@ package net.corda.core.cordapp import net.corda.core.DoNotImplement import net.corda.core.flows.FlowLogic import net.corda.core.schemas.MappedSchema +import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializeAsToken import java.net.URL @@ -22,6 +23,8 @@ import java.net.URL * @property schedulableFlows List of flows startable by the scheduler * @property services List of RPC services * @property serializationWhitelists List of Corda plugin registries + * @property serializationCustomSerializerProxies List of Proxy classes used by the custom serializers + * @property serializationCustomSerializers List of serializers * @property customSchemas List of custom schemas * @property jarPath The path to the JAR for this CorDapp */ @@ -35,6 +38,8 @@ interface Cordapp { val schedulableFlows: List>> val services: List> val serializationWhitelists: List + val serializationCustomSerializerProxies: List> + val serializationCustomSerializers: List> val customSchemas: Set val jarPath: URL val cordappClasses: List diff --git a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt index 02410db37a..7aaab3b8b3 100644 --- a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt +++ b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappImpl.kt @@ -3,6 +3,7 @@ package net.corda.core.internal.cordapp import net.corda.core.cordapp.Cordapp import net.corda.core.flows.FlowLogic import net.corda.core.schemas.MappedSchema +import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializeAsToken import java.io.File @@ -16,6 +17,8 @@ data class CordappImpl( override val schedulableFlows: List>>, override val services: List>, override val serializationWhitelists: List, + override val serializationCustomSerializerProxies: List>, + override val serializationCustomSerializers: List>, override val customSchemas: Set, override val jarPath: URL) : Cordapp { override val name: String = File(jarPath.toURI()).name.removeSuffix(".jar") diff --git a/core/src/main/kotlin/net/corda/core/serialization/CordaCustomSerializer.kt b/core/src/main/kotlin/net/corda/core/serialization/CordaCustomSerializer.kt new file mode 100644 index 0000000000..724e8d7db8 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/serialization/CordaCustomSerializer.kt @@ -0,0 +1,8 @@ +package net.corda.core.serialization + +@Target(AnnotationTarget.CLASS) +annotation class CordaCustomSerializer + +@Target(AnnotationTarget.CLASS) +annotation class CordaCustomSerializerProxy + diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt new file mode 100644 index 0000000000..0f384196f5 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationCustomSerializer.kt @@ -0,0 +1,46 @@ +package net.corda.core.serialization + +import java.lang.reflect.Type + +/** + * Allows CorDapps to provide custom serializers for third party libraries where those libraries cannot + * be recompiled with the -parmater flag rendering their classes natively serializable by Corda. In this case + * a proxy serializer can be written that extends this type whose purpose is to move between those an + * unserializable types and an intermediate representation + * + * NOTE: The proxy object must be specified as a seperate class. However, this can be defined within the + * scope of the serializer. Also, that class must be annotated with the [CordaCustomSerializerProxy] + * annotation + * + * For instances of this class to be discoverable they must be annotated with the [CordaCustomSerializer] + * annotation. + * + * Failing to apply either annotation will result in the class not being loaded by Corda and thus serialization + * failing + * + * @property type the type of the object that this class is proxying + * @property ptype the type of the proxy object used as an intermediate representation of [type] + */ +interface SerializationCustomSerializer { + /** + * Should facilitate the conversion of the third party object into the serializable + * local class specified by [ptype] + */ + fun toProxy(obj: Any) : Any + + /** + * Should facilitate the conversion of the proxy object into a new instance of the + * unserializable type + */ + fun fromProxy(proxy: Any) : Any + + /** + * Should be set to the type of the object being proxied + */ + val type: Type + + /** + * Should be set to the proxy objects type + */ + val ptype: Type +} \ No newline at end of file diff --git a/docs/source/cordapp-custom-serializers.rst b/docs/source/cordapp-custom-serializers.rst new file mode 100644 index 0000000000..e5e38e32b8 --- /dev/null +++ b/docs/source/cordapp-custom-serializers.rst @@ -0,0 +1,69 @@ +Pluggable Serializers for CorDapps +================================== + +To be serializable by Corda Java classes must be compiled with the -parameter switch to enable mathcing of ass property +to constructor parameter. However, when this isn't possible CorDapps can provide custom proxy serialiszers that Corda +can use to move from types it cannot serialiser to an interim represtnation that it can with the transformation to and +from this proxy object being handled by the supplied serialiser. + +Serializer Location +------------------- +Custom serializers should be placed in the plugins directory fo a CorDapp or a sub directory (placing it in a sub +directory however does require that directory be added to the list of locations scanned within the jar) + +Writing a Custom Serializer +-------------------------- + +Serializers must + * Inherit from net.corda.core.serialization.SerializationCustomSerializer + * Be annotated with the @CordaCustomSerializer annotation + * Provide a proxy class to transform the objectto and from + * Have that proxy class annotated with the @CordaCustomSerializerProxy annotation + +Serializers inheriting from SerializationCustomSerializer have to implement two methods and two types + +Example +------- + +Consider this example class + +.. sourcecode:: java + public final class Example { + private final Int a + private final Int b + + private Example(Int a, Int b) { + this.a = a; + this.b = b; + } + + public static Example of (int[] a) { return Example(a[0], a[1]); } + + public int getA() { return a; } + public int getB() { return b; } + } + +This would require a serialiser as follows + +.. sourcecode:: kotlin + @CordaCustomSerializer + class ExampleSerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val a: Int, val b: Int) + + override fun toProxy(obj: Any): Any = Proxy((obj as Example).a, obj.b) + + override fun fromProxy(proxy: Any): Any { + val constructorArg = IntArray(2); + constructorArg[0] = (proxy as Proxy).a + constructorArg[1] = proxy.b + return Example.create(constructorArg) + } + + override val type: Type get() = Example::class.java + override val ptype: Type get() = Proxy::class.java + } + + + + diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 36aeb6c6c5..631a20a628 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -12,6 +12,20 @@ Unreleased That is the ability to alter an enum constant and, as long as certain rules are followed and the correct annotations applied, have older and newer instances of that enumeration be understood. +* **AMQP Enabled** + +AMQP Serialization is now enabled for both peer to peer communication and writing states to the vault. This change +brings a stable format Corda can support internally throughout it's lifetime that meets the needs of Corda and our +users. + +* **CorDapp Custom Serializers** + +To allow interop with third party libraries that cannot be recompiled we add functionality that allows custom serialises +to be written for those classes provided. If needed, a proxy object can be created as an interim step that allows Corda's internal +serialisers to operate on those types. + +A good example of this is the SIMM valuation demo which has a number of such serializers defined in the plugin/customserializers package + Release 2.0 ---------- Following quickly on the heels of the release of Corda 1.0, Corda version 2.0 consolidates diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt index 4327d114ea..62172dc12c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.cordapp.Cordapp import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence import net.corda.nodeapi.internal.serialization.DefaultWhitelist @@ -24,7 +25,8 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) { } } -abstract class AbstractAMQPSerializationScheme : SerializationScheme { +abstract class AbstractAMQPSerializationScheme(val cordappLoader: List) : SerializationScheme { + companion object { private val serializationWhitelists: List by lazy { ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist @@ -62,8 +64,15 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme { register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(this)) register(net.corda.nodeapi.internal.serialization.amqp.custom.ContractAttachmentSerializer(this)) } - for (whitelistProvider in serializationWhitelists) + for (whitelistProvider in serializationWhitelists) { factory.addToWhitelist(*whitelistProvider.whitelist.toTypedArray()) + } + + cordappLoader.forEach { ca -> + ca.serializationCustomSerializers.forEach { + factory.registerExternal(CorDappCustomSerializer(it.newInstance(), factory)) + } + } } private val serializerFactoriesForContexts = ConcurrentHashMap, SerializerFactory>() @@ -97,11 +106,11 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme { return SerializationOutput(serializerFactory).serialize(obj) } - protected fun canDeserializeVersion(byteSequence: ByteSequence): Boolean = AMQP_ENABLED && byteSequence == AmqpHeaderV1_0 + protected fun canDeserializeVersion(byteSequence: ByteSequence): Boolean = byteSequence == AmqpHeaderV1_0 } // TODO: This will eventually cover server RPC as well and move to node module, but for now this is not implemented -class AMQPServerSerializationScheme : AbstractAMQPSerializationScheme() { +class AMQPServerSerializationScheme(cordapps: List = emptyList()) : AbstractAMQPSerializationScheme(cordapps) { override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { throw UnsupportedOperationException() } @@ -118,7 +127,7 @@ class AMQPServerSerializationScheme : AbstractAMQPSerializationScheme() { } // TODO: This will eventually cover client RPC as well and move to client module, but for now this is not implemented -class AMQPClientSerializationScheme : AbstractAMQPSerializationScheme() { +class AMQPClientSerializationScheme(cordapps: List = emptyList()) : AbstractAMQPSerializationScheme(cordapps) { override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt new file mode 100644 index 0000000000..87be4312d1 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt @@ -0,0 +1,41 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.SerializationCustomSerializer +import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType +import org.apache.qpid.proton.amqp.Symbol +import org.apache.qpid.proton.codec.Data +import java.lang.reflect.Type + +class CorDappCustomSerializer( + private val serialiser: SerializationCustomSerializer, + factory: SerializerFactory) + : AMQPSerializer, SerializerFor { + override val revealSubclassesInSchema: Boolean get() = false + override val type: Type get() = serialiser.type + override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(type)}") + val descriptor: Descriptor = Descriptor(typeDescriptor) + + private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(serialiser.ptype, factory) } + + override fun writeClassInfo(output: SerializationOutput) {} + + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { + val proxy = serialiser.toProxy(obj) + + data.withDescribed(descriptor) { + data.withList { + for (property in proxySerializer.propertySerializers) { + property.writeProperty(proxy, this, output) + } + } + } + } + + override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any { + return serialiser.fromProxy(uncheckedCast(proxySerializer.readObject(obj, schema, input))) + } + + override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == type +} + 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 870bfbaccc..d2180b7518 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 @@ -6,22 +6,27 @@ import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type +interface SerializerFor { + /** + * This method should return true if the custom serializer can serialize an instance of the class passed as the + * parameter. + */ + fun isSerializerFor(clazz: Class<*>): Boolean + + val revealSubclassesInSchema: Boolean +} + /** * Base class for serializers of core platform types that do not conform to the usual serialization rules and thus * cannot be automatically serialized. */ -abstract class CustomSerializer : AMQPSerializer { +abstract class CustomSerializer : AMQPSerializer, SerializerFor { /** * This is a collection of custom serializers that this custom serializer depends on. e.g. for proxy objects * that refer to other custom types etc. */ open val additionalSerializers: Iterable> = emptyList() - /** - * This method should return true if the custom serializer can serialize an instance of the class passed as the - * parameter. - */ - abstract fun isSerializerFor(clazz: Class<*>): Boolean protected abstract val descriptor: Descriptor /** @@ -33,7 +38,7 @@ abstract class CustomSerializer : AMQPSerializer { /** * Whether subclasses using this serializer via inheritance should have a mapping in the schema. */ - open val revealSubclassesInSchema: Boolean = false + override val revealSubclassesInSchema: Boolean get() = false override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { data.withDescribed(descriptor) { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index 6a1377d083..8b677630d2 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -82,11 +82,13 @@ private fun propertiesForSerializationFromConstructor(kotlinConstructo for (param in kotlinConstructor.parameters) { val name = param.name ?: throw NotSerializableException("Constructor parameter of $clazz has no name.") val matchingProperty = properties[name] ?: - throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'." + - " If using Java, check that you have the -parameters option specified in the Java compiler.") + throw NotSerializableException("No property matching constructor parameter named '$name' of '$clazz'. " + + "If using Java, check that you have the -parameters option specified in the Java compiler. " + + "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") // Check that the method has a getter in java. - val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz." + - " If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler.") + val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz. " + + "If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler." + + "Alternately, provide a proxy serializer (SerializationCustomSerializer) if recompiling isn't an option") val returnType = resolveTypeVariables(getter.genericReturnType, type) if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) { rc += PropertySerializer.make(name, getter, returnType, factory) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index 39abcc58f5..0f091029cb 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -36,7 +36,7 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { private val serializersByType = ConcurrentHashMap>() private val serializersByDescriptor = ConcurrentHashMap>() - private val customSerializers = CopyOnWriteArrayList>() + private val customSerializers = CopyOnWriteArrayList() val transformsCache = ConcurrentHashMap>>() open val classCarpenter = ClassCarpenter(cl, whitelist) @@ -196,6 +196,13 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { } } + fun registerExternal(customSerializer: CorDappCustomSerializer) { + if (!serializersByDescriptor.containsKey(customSerializer.typeDescriptor)) { + customSerializers += customSerializer + serializersByDescriptor[customSerializer.typeDescriptor] = customSerializer + } + } + /** * Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd * if not use the [ClassCarpenter] to generate a class to use in it's place @@ -267,11 +274,13 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { for (customSerializer in customSerializers) { if (customSerializer.isSerializerFor(clazz)) { val declaredSuperClass = declaredType.asClass()?.superclass - if (declaredSuperClass == null || !customSerializer.isSerializerFor(declaredSuperClass) || !customSerializer.revealSubclassesInSchema) { - return customSerializer + return if (declaredSuperClass == null + || !customSerializer.isSerializerFor(declaredSuperClass) + || !customSerializer.revealSubclassesInSchema) { + customSerializer as? AMQPSerializer } else { // Make a subclass serializer for the subclass and return that... - return CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer)) + CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer)) } } } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt new file mode 100644 index 0000000000..213bc37a44 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt @@ -0,0 +1,92 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import org.junit.Test +import net.corda.core.serialization.CordaCustomSerializer +import net.corda.core.serialization.CordaCustomSerializerProxy +import net.corda.core.serialization.SerializationCustomSerializer +import java.lang.reflect.Type +import kotlin.test.assertEquals + +class CorDappSerializerTests { + data class NeedsProxy (val a: String) + + @CordaCustomSerializer + class NeedsProxyProxySerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val proxy_a_: String) + + override val type: Type get() = NeedsProxy::class.java + override val ptype: Type get() = Proxy::class.java + + override fun fromProxy(proxy: Any) : Any { + println ("NeedsProxyProxySerialiser - fromProxy") + return NeedsProxy((proxy as Proxy).proxy_a_) + } + override fun toProxy(obj: Any) : Any { + println ("NeedsProxyProxySerialiser - to Proxy") + return Proxy((obj as NeedsProxy).a) + } + } + + // Standard proxy serialiser used internally, here for comparison purposes + class InternalProxySerialiser(factory: SerializerFactory) : + CustomSerializer.Proxy ( + NeedsProxy::class.java, + InternalProxySerialiser.Proxy::class.java, + factory) { + data class Proxy(val proxy_a_: String) + + override fun toProxy(obj: NeedsProxy): Proxy { + println ("InternalProxySerialiser - toProxy") + return Proxy(obj.a) + } + + override fun fromProxy(proxy: Proxy): NeedsProxy { + println ("InternalProxySerialiser - fromProxy") + return NeedsProxy(proxy.proxy_a_) + } + } + + @Test + fun `type uses proxy`() { + val internalProxyFactory = testDefaultFactory() + val proxyFactory = testDefaultFactory() + val defaultFactory = testDefaultFactory() + + val msg = "help" + + proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory)) + internalProxyFactory.register (InternalProxySerialiser(internalProxyFactory)) + + val needsProxy = NeedsProxy(msg) + + val bAndSProxy = SerializationOutput(proxyFactory).serializeAndReturnSchema (needsProxy) + val bAndSInternal = SerializationOutput(internalProxyFactory).serializeAndReturnSchema (needsProxy) + val bAndSDefault = SerializationOutput(defaultFactory).serializeAndReturnSchema (needsProxy) + + val objFromDefault = DeserializationInput(defaultFactory).deserializeAndReturnEnvelope(bAndSDefault.obj) + val objFromInternal = DeserializationInput(internalProxyFactory).deserializeAndReturnEnvelope(bAndSInternal.obj) + val objFromProxy = DeserializationInput(proxyFactory).deserializeAndReturnEnvelope(bAndSProxy.obj) + + assertEquals(msg, objFromDefault.obj.a) + assertEquals(msg, objFromInternal.obj.a) + assertEquals(msg, objFromProxy.obj.a) + } + + @Test + fun proxiedTypeIsNested() { + data class A (val a: Int, val b: NeedsProxy) + + val factory = testDefaultFactory() + factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory)) + + val tv1 = 100 + val tv2 = "pants schmants" + val bAndS = SerializationOutput(factory).serializeAndReturnSchema (A(tv1, NeedsProxy(tv2))) + + val objFromDefault = DeserializationInput(factory).deserializeAndReturnEnvelope(bAndS.obj) + + assertEquals(tv1, objFromDefault.obj.a) + assertEquals(tv2, objFromDefault.obj.b.a) + } +} \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt index 108f2328f4..2b6ee350f6 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt @@ -25,7 +25,7 @@ class OverridePKSerializerTest { get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. } - class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme() { + class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } 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 04c390202d..9a987f1b8a 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 @@ -593,7 +593,7 @@ class SerializationOutputTests { fun `test transaction state`() { val state = TransactionState(FooState(), FOO_PROGRAM_ID, MEGA_CORP) - val scheme = AMQPServerSerializationScheme() + val scheme = AMQPServerSerializationScheme(emptyList()) val func = scheme::class.superclasses.single { it.simpleName == "AbstractAMQPSerializationScheme" } .java.getDeclaredMethod("registerCustomSerializers", SerializerFactory::class.java) func.isAccessible = true diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 2ddba4a393..fd13f32fc0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -300,11 +300,11 @@ open class Node(configuration: NodeConfiguration, nodeSerializationEnv = SerializationEnvironmentImpl( SerializationFactoryImpl().apply { registerScheme(KryoServerSerializationScheme()) - registerScheme(AMQPServerSerializationScheme()) + registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps)) }, - KRYO_P2P_CONTEXT.withClassLoader(classloader), + p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader), rpcServerContext = KRYO_RPC_SERVER_CONTEXT.withClassLoader(classloader), - storageContext = KRYO_STORAGE_CONTEXT.withClassLoader(classloader), + storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader), checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader)) } diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 4863471957..7dc11cbeb7 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -10,9 +10,8 @@ import net.corda.core.internal.* import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.node.services.CordaService import net.corda.core.schemas.MappedSchema -import net.corda.core.serialization.SerializationWhitelist -import net.corda.core.serialization.SerializeAsToken import net.corda.core.utilities.contextLogger +import net.corda.core.serialization.* import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.serialization.DefaultWhitelist @@ -175,15 +174,17 @@ class CordappLoader private constructor(private val cordappJarPaths: List> { + return scanResult.getClassesWithAnnotation(Class::class, CordaCustomSerializerProxy::class) + } + + private fun findSerialzers(scanResult: RestrictedScanResult) : List> { + return scanResult.getClassesWithAnnotation(SerializationCustomSerializer::class, CordaCustomSerializer::class) + } + private fun findCustomSchemas(scanResult: RestrictedScanResult): Set { return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet() } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt index 3b7b78a374..16590715fd 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt @@ -239,4 +239,4 @@ private fun readMap(buffer: BufferInput>, serializer: Seriali put(serializer.readObject(buffer), serializer.readObject(buffer)) } } -} \ No newline at end of file +} diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index ee92826c94..be6c0e17ff 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -27,7 +27,7 @@ class SimmValuationTest { @Test fun `runs SIMM valuation demo`() { - driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.vega.contracts")) { + driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.vega.contracts", "net.corda.vega.plugin.customserializers")) { val nodeAFuture = startNode(providedName = nodeALegalName) val nodeBFuture = startNode(providedName = nodeBLegalName) val (nodeA, nodeB) = listOf(nodeAFuture, nodeBFuture).map { it.getOrThrow() } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index 01de81585b..8b9b1d7414 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -19,7 +19,7 @@ val PORTFOLIO_SWAP_PROGRAM_ID = "net.corda.vega.contracts.PortfolioSwap" * given point in time. This state can be consumed to create a new state with a mutated valuation or portfolio. */ data class PortfolioState(val portfolio: List, - private val _parties: Pair, + val _parties: Pair, val valuationDate: LocalDate, val valuation: PortfolioValuation? = null, override val linearId: UniqueIdentifier = UniqueIdentifier()) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPluginRegistry.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPluginRegistry.kt index 626de57761..2c8bc6f473 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPluginRegistry.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPluginRegistry.kt @@ -10,6 +10,7 @@ import com.opengamma.strata.market.curve.CurveName import com.opengamma.strata.market.param.CurrencyParameterSensitivities import com.opengamma.strata.market.param.CurrencyParameterSensitivity import com.opengamma.strata.market.param.TenorDateParameterMetadata +import com.opengamma.strata.market.param.ParameterMetadata import net.corda.core.serialization.SerializationWhitelist import net.corda.vega.analytics.CordaMarketData import net.corda.vega.analytics.InitialMarginTriple @@ -34,6 +35,7 @@ class SimmPluginRegistry : SerializationWhitelist { DoubleArray::class.java, CurveName::class.java, TenorDateParameterMetadata::class.java, - Tenor::class.java + Tenor::class.java, + ParameterMetadata::class.java ) } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencyParameterSensitivitiesSerializer.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencyParameterSensitivitiesSerializer.kt new file mode 100644 index 0000000000..5d77d98e3d --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencyParameterSensitivitiesSerializer.kt @@ -0,0 +1,21 @@ +package net.corda.vega.plugin.customserializers + +import com.opengamma.strata.market.param.CurrencyParameterSensitivities +import com.opengamma.strata.market.param.CurrencyParameterSensitivity +import net.corda.core.serialization.CordaCustomSerializer +import net.corda.core.serialization.CordaCustomSerializerProxy +import net.corda.core.serialization.SerializationCustomSerializer +import java.lang.reflect.Type + +@CordaCustomSerializer +@Suppress("UNUSED") +class CurrencyParameterSensitivitiesSerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val sensitivities: List) + + override val type: Type get() = CurrencyParameterSensitivities::class.java + override val ptype: Type get() = Proxy::class.java + + override fun fromProxy(proxy: Any): Any = CurrencyParameterSensitivities.of ((proxy as Proxy).sensitivities) + override fun toProxy(obj: Any): Any = Proxy ((obj as CurrencyParameterSensitivities).sensitivities) +} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencyParameterSensitivitySerialiser.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencyParameterSensitivitySerialiser.kt new file mode 100644 index 0000000000..7de99cac66 --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencyParameterSensitivitySerialiser.kt @@ -0,0 +1,33 @@ +package net.corda.vega.plugin.customserializers + +import com.opengamma.strata.market.param.CurrencyParameterSensitivity +import com.opengamma.strata.market.param.ParameterMetadata +import com.opengamma.strata.data.MarketDataName +import com.opengamma.strata.collect.array.DoubleArray +import com.opengamma.strata.basics.currency.Currency +import net.corda.core.serialization.CordaCustomSerializer +import net.corda.core.serialization.CordaCustomSerializerProxy +import net.corda.core.serialization.SerializationCustomSerializer +import java.lang.reflect.Type + +@CordaCustomSerializer +@Suppress("UNUSED") +class CurrencyParameterSensitivitySerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val currency: Currency, val marketDataName: MarketDataName<*>, + val parameterMetadata: List, + val sensitivity: DoubleArray) + + override val type: Type get() = CurrencyParameterSensitivity::class.java + override val ptype: Type get() = Proxy::class.java + + override fun fromProxy(proxy: Any): Any = + CurrencyParameterSensitivity.of( + (proxy as Proxy).marketDataName, + proxy.parameterMetadata, + proxy.currency, + proxy.sensitivity) + + override fun toProxy(obj: Any): Any = Proxy ((obj as CurrencyParameterSensitivity).currency, + obj.marketDataName, obj.parameterMetadata, obj.sensitivity) +} diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencySerializer.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencySerializer.kt new file mode 100644 index 0000000000..19f4de4195 --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/CurrencySerializer.kt @@ -0,0 +1,20 @@ +package net.corda.vega.plugin.customserializers + +import com.opengamma.strata.basics.currency.Currency +import net.corda.core.serialization.CordaCustomSerializer +import net.corda.core.serialization.CordaCustomSerializerProxy +import net.corda.core.serialization.SerializationCustomSerializer +import java.lang.reflect.Type + +@CordaCustomSerializer +@Suppress("UNUSED") +class CurrencySerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val currency: String) + + override val type: Type get() = Currency::class.java + override val ptype: Type get() = Proxy::class.java + + override fun fromProxy(proxy: Any): Any = Currency.parse((proxy as Proxy).currency) + override fun toProxy(obj: Any): Any = Proxy((obj as Currency).toString()) +} diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/DoubleArraySerializer.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/DoubleArraySerializer.kt new file mode 100644 index 0000000000..e429fc9d10 --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/DoubleArraySerializer.kt @@ -0,0 +1,20 @@ +package net.corda.vega.plugin.customserializers + +import net.corda.core.serialization.CordaCustomSerializer +import net.corda.core.serialization.CordaCustomSerializerProxy +import net.corda.core.serialization.SerializationCustomSerializer +import com.opengamma.strata.collect.array.DoubleArray +import java.lang.reflect.Type + +@CordaCustomSerializer +@Suppress("UNUSED") +class DoubleArraySerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val amount: kotlin.DoubleArray) + + override val type: Type get() = DoubleArray::class.java + override val ptype: Type get() = Proxy::class.java + + override fun fromProxy(proxy: Any): Any = DoubleArray.copyOf((proxy as Proxy).amount) + override fun toProxy(obj: Any): Any = Proxy((obj as DoubleArray).toArray()) +} diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/MultiCurrencyAmountSerializer.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/MultiCurrencyAmountSerializer.kt new file mode 100644 index 0000000000..fed97f7358 --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/MultiCurrencyAmountSerializer.kt @@ -0,0 +1,22 @@ +package net.corda.vega.plugin.customserializers + +import com.opengamma.strata.basics.currency.MultiCurrencyAmount +import com.opengamma.strata.basics.currency.Currency +import net.corda.core.serialization.* +import java.lang.reflect.Type + +@CordaCustomSerializer +@Suppress("UNUSED") +class MultiCurrencyAmountSerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val curencies : Map) + + override fun toProxy(obj: Any): Any = Proxy((obj as MultiCurrencyAmount).toMap()) + override fun fromProxy(proxy: Any): Any = MultiCurrencyAmount.of((proxy as Proxy).curencies) + + override val type: Type get() = MultiCurrencyAmount::class.java + override val ptype: Type get() = Proxy::class.java +} + + + diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/TenorDateParameterMetadataSerializer.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/TenorDateParameterMetadataSerializer.kt new file mode 100644 index 0000000000..84aac96da8 --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/TenorDateParameterMetadataSerializer.kt @@ -0,0 +1,23 @@ +package net.corda.vega.plugin.customserializers + +import com.opengamma.strata.basics.date.Tenor +import com.opengamma.strata.market.param.TenorDateParameterMetadata +import net.corda.core.serialization.* +import java.lang.reflect.Type +import java.time.LocalDate + +@CordaCustomSerializer +@Suppress("UNUSED") +class TenorDateParameterMetadataSerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val tenor: Tenor, val date: LocalDate, val identifier: Tenor, val label: String) + + override val type: Type get() = TenorDateParameterMetadata::class.java + override val ptype: Type get() = Proxy::class.java + + override fun toProxy(obj: Any): Any = Proxy( + (obj as TenorDateParameterMetadata).tenor, obj.date, obj.identifier, obj.label) + + override fun fromProxy(proxy: Any): Any = TenorDateParameterMetadata.of( + (proxy as Proxy).date, proxy.tenor, proxy.label) +} diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/TenorSerializer.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/TenorSerializer.kt new file mode 100644 index 0000000000..8fc397ee34 --- /dev/null +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/customserializers/TenorSerializer.kt @@ -0,0 +1,21 @@ +package net.corda.vega.plugin.customserializers + +import com.opengamma.strata.basics.date.Tenor +import net.corda.core.serialization.* +import java.lang.reflect.Type +import java.time.Period + +@CordaCustomSerializer +@Suppress("UNUSED") +class TenorSerializer : SerializationCustomSerializer { + @CordaCustomSerializerProxy + data class Proxy(val years: Int, val months: Int, val days: Int, val name: String) + + override val type: Type get() = Tenor::class.java + override val ptype: Type get() = Proxy::class.java + + override fun toProxy(obj: Any): Any = Proxy( + (obj as Tenor).period.years, obj.period.months, obj.period.days, obj.toString()) + + override fun fromProxy(proxy: Any): Any = Tenor.of (Period.of((proxy as Proxy).years, proxy.months, proxy.days)) +} diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index aa3776c02c..0d21e8c1ed 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -47,6 +47,8 @@ import net.corda.testing.testNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils import rx.internal.schedulers.CachedThreadScheduler +import org.slf4j.Logger +import java.io.Closeable import java.math.BigInteger import java.nio.file.Path import java.security.KeyPair diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt index 17f9a83295..8581534d0f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt @@ -62,7 +62,7 @@ fun withTestSerialization(inheritable: Boolean = false, callable: (Serializa /** * For example your test class uses [SerializationEnvironmentRule] but you want to turn it off for one method. - * Use sparingly, ideally a test class shouldn't mix serialization init mechanisms. + * Use sparingly, ideally a test class shouldn't mix serializers init mechanisms. */ fun withoutTestSerialization(callable: () -> T): T { val (property, env) = listOf(_contextSerializationEnv, _inheritableContextSerializationEnv).map { Pair(it, it.get()) }.single { it.second != null } @@ -99,13 +99,12 @@ private fun createTestSerializationEnv(label: String) = object : SerializationEn SerializationFactoryImpl().apply { registerScheme(KryoClientSerializationScheme()) registerScheme(KryoServerSerializationScheme()) - registerScheme(AMQPClientSerializationScheme()) - registerScheme(AMQPServerSerializationScheme()) + registerScheme(AMQPClientSerializationScheme(emptyList())) + registerScheme(AMQPServerSerializationScheme(emptyList())) }, - if (isAmqpEnabled()) AMQP_P2P_CONTEXT else KRYO_P2P_CONTEXT, + AMQP_P2P_CONTEXT, KRYO_RPC_SERVER_CONTEXT, - KRYO_RPC_CLIENT_CONTEXT, - if (isAmqpEnabled()) AMQP_STORAGE_CONTEXT else KRYO_STORAGE_CONTEXT, + AMQP_STORAGE_CONTEXT, KRYO_CHECKPOINT_CONTEXT) { override fun toString() = "testSerializationEnv($label)" } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt index 5c8736335e..aa8e19965f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt @@ -14,7 +14,18 @@ class MockCordappProvider(cordappLoader: CordappLoader, attachmentStorage: Attac val cordappRegistry = mutableListOf>() fun addMockCordapp(contractClassName: ContractClassName, attachments: MockAttachmentStorage) { - val cordapp = CordappImpl(listOf(contractClassName), emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), Paths.get(".").toUri().toURL()) + val cordapp = CordappImpl( + contractClassNames = listOf(contractClassName), + initiatedFlows = emptyList(), + rpcFlows = emptyList(), + serviceFlows = emptyList(), + schedulableFlows = emptyList(), + services = emptyList(), + serializationWhitelists = emptyList(), + serializationCustomSerializerProxies = emptyList(), + serializationCustomSerializers = emptyList(), + customSchemas = emptySet(), + jarPath = Paths.get(".").toUri().toURL()) if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) { cordappRegistry.add(Pair(cordapp, findOrImportAttachment(contractClassName.toByteArray(), attachments))) } diff --git a/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt b/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt index 5db494cad1..81571dd722 100644 --- a/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt +++ b/verifier/src/main/kotlin/net/corda/verifier/Verifier.kt @@ -91,11 +91,7 @@ class Verifier { registerScheme(KryoVerifierSerializationScheme) registerScheme(AMQPVerifierSerializationScheme) }, - /** - * Even though default context is set to Kryo P2P, the encoding will be adjusted depending on the incoming - * request received, see use of [context] in [main] method. - */ - KRYO_P2P_CONTEXT) + AMQP_P2P_CONTEXT) } } @@ -108,7 +104,7 @@ class Verifier { override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException() } - private object AMQPVerifierSerializationScheme : AbstractAMQPSerializationScheme() { + private object AMQPVerifierSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { return (byteSequence == AmqpHeaderV1_0 && (target == SerializationContext.UseCase.P2P)) } @@ -116,4 +112,4 @@ class Verifier { override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory = throw UnsupportedOperationException() override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory = throw UnsupportedOperationException() } -} \ No newline at end of file +}