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 67eab59f15a..6132509e21f 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 @@ -6,7 +6,7 @@ import net.corda.nodeapi.internal.serialization.CordaSerializationMagic import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.nodeSerializationEnv -import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT +import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme @@ -43,7 +43,7 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() { registerScheme(KryoClientSerializationScheme()) registerScheme(AMQPClientSerializationScheme(emptyList())) }, - KRYO_P2P_CONTEXT, + AMQP_P2P_CONTEXT, rpcClientContext = KRYO_RPC_CLIENT_CONTEXT) } } diff --git a/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt b/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt index 89c4ee9092b..f725babdf25 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/KotlinUtilsTest.kt @@ -6,7 +6,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.serialization.KRYO_CHECKPOINT_CONTEXT -import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.junit.Rule @@ -39,14 +38,6 @@ class KotlinUtilsTest { assertThat(copy.transientVal).isEqualTo(copyVal) } - @Test - fun `serialise transient property with non-capturing lambda`() { - expectedEx.expect(KryoException::class.java) - expectedEx.expectMessage("is not annotated or on the whitelist, so cannot be used in serialization") - val original = NonCapturingTransientProperty() - original.serialize(context = KRYO_P2P_CONTEXT) - } - @Test fun `deserialise transient property with non-capturing lambda`() { expectedEx.expect(KryoException::class.java) @@ -66,14 +57,6 @@ class KotlinUtilsTest { assertThat(copy.transientVal).startsWith("Hello") } - @Test - fun `serialise transient property with capturing lambda`() { - expectedEx.expect(KryoException::class.java) - expectedEx.expectMessage("is not annotated or on the whitelist, so cannot be used in serialization") - val original = CapturingTransientProperty("Hello") - original.serialize(context = KRYO_P2P_CONTEXT) - } - @Test fun `deserialise transient property with capturing lambda`() { expectedEx.expect(KryoException::class.java) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt index aa939c5002b..c18d7f4228c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/ServerContexts.kt @@ -29,14 +29,7 @@ val KRYO_RPC_SERVER_CONTEXT = SerializationContextImpl(kryoMagic, true, SerializationContext.UseCase.RPCServer, null) -val KRYO_STORAGE_CONTEXT = SerializationContextImpl(kryoMagic, - SerializationDefaults.javaClass.classLoader, - AllButBlacklisted, - emptyMap(), - true, - SerializationContext.UseCase.Storage, - null, - AlwaysAcceptEncodingWhitelist) + val AMQP_STORAGE_CONTEXT = SerializationContextImpl(amqpMagic, SerializationDefaults.javaClass.classLoader, AllButBlacklisted, @@ -45,6 +38,7 @@ val AMQP_STORAGE_CONTEXT = SerializationContextImpl(amqpMagic, SerializationContext.UseCase.Storage, null, AlwaysAcceptEncodingWhitelist) + val AMQP_RPC_SERVER_CONTEXT = SerializationContextImpl(amqpMagic, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SharedContexts.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SharedContexts.kt index 25977e4c1ef..e54c70cc69a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SharedContexts.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SharedContexts.kt @@ -16,14 +16,6 @@ import net.corda.nodeapi.internal.serialization.kryo.kryoMagic * CANNOT always be instantiated outside of the server and so * MUST be kept separate from these ones! */ - -val KRYO_P2P_CONTEXT = SerializationContextImpl(kryoMagic, - SerializationDefaults.javaClass.classLoader, - GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), - emptyMap(), - true, - SerializationContext.UseCase.P2P, - null) val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(kryoMagic, SerializationDefaults.javaClass.classLoader, QuasarWhitelist, @@ -32,6 +24,7 @@ val KRYO_CHECKPOINT_CONTEXT = SerializationContextImpl(kryoMagic, SerializationContext.UseCase.Checkpoint, null, AlwaysAcceptEncodingWhitelist) + val AMQP_P2P_CONTEXT = SerializationContextImpl(amqpMagic, SerializationDefaults.javaClass.classLoader, GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()), 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 8d07afcace8..f099ce7af58 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,13 +2,16 @@ package net.corda.nodeapi.internal.serialization.amqp +import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import net.corda.core.cordapp.Cordapp +import net.corda.core.internal.objectOrNewInstance import net.corda.core.serialization.* import net.corda.nodeapi.internal.serialization.CordaSerializationMagic import net.corda.core.utilities.ByteSequence import net.corda.nodeapi.internal.serialization.DefaultWhitelist import net.corda.nodeapi.internal.serialization.MutableClassWhitelist import net.corda.nodeapi.internal.serialization.SerializationScheme +import java.lang.reflect.Modifier import java.security.PublicKey import java.util.* import java.util.concurrent.ConcurrentHashMap @@ -28,10 +31,20 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) { abstract class AbstractAMQPSerializationScheme(val cordappLoader: List) : SerializationScheme { + // TODO: This method of initialisation for the Whitelist and plugin serializers will have to change + // when we have per-cordapp contexts and dynamic app reloading but for now it's the easiest way companion object { private val serializationWhitelists: List by lazy { ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist } + + private val customSerializers: List> by lazy { + FastClasspathScanner().addClassLoader(this::class.java.classLoader).scan() + .getNamesOfClassesImplementing(SerializationCustomSerializer::class.java) + .mapNotNull { this::class.java.classLoader.loadClass(it).asSubclass(SerializationCustomSerializer::class.java) } + .filterNot { Modifier.isAbstract(it.modifiers) } + .map { it.kotlin.objectOrNewInstance() } + } } private fun registerCustomSerializers(factory: SerializerFactory) { @@ -69,11 +82,20 @@ abstract class AbstractAMQPSerializationScheme(val cordappLoader: List) factory.addToWhitelist(*whitelistProvider.whitelist.toTypedArray()) } - for (loader in cordappLoader) { - for (schema in loader.serializationCustomSerializers) { - factory.registerExternal(CorDappCustomSerializer(schema, factory)) + // If we're passed in an external list we trust that, otherwise revert to looking at the scan of the + // classpath to find custom serializers. + if (cordappLoader.isEmpty()) { + for (customSerializer in customSerializers) { + factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) + } + } else { + cordappLoader.forEach { loader -> + for (customSerializer in loader.serializationCustomSerializers) { + factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) + } } } + } private val serializerFactoriesForContexts = ConcurrentHashMap, SerializerFactory>()