RPC Kryo plugin functionality.

This commit is contained in:
rick.parker 2016-11-15 17:16:33 +00:00
parent f90c5a702a
commit 6abb5750bf
6 changed files with 69 additions and 5 deletions

View File

@ -1,5 +1,7 @@
package net.corda.core.node
import com.esotericsoftware.kryo.Kryo
/**
* Implement this interface on a class advertised in a META-INF/services/net.corda.core.node.CordaPluginRegistry file
* to extend a Corda node with additional application services.
@ -35,4 +37,16 @@ abstract class CordaPluginRegistry {
* allow access to the protocol factory and protocol initiation entry points there.
*/
open val servicePlugins: List<Class<*>> = emptyList()
/**
* Optionally register types with [Kryo] for use over RPC, as we lock down the types that can be serialised in this
* particular use case.
* For example, if you add an RPC interface that carries some contract states back and forth, you need to register
* those classes here using the [register] method on Kryo.
*
* TODO: Kryo and likely the requirement to register classes here will go away when we replace the serialization implementation.
*
* @return true if you register types, otherwise you will be filtered out of the list of plugins considered in future.
*/
open fun registerRPCKryoTypes(kryo: Kryo): Boolean = false
}

View File

@ -80,5 +80,17 @@ Wire protocol
The client RPC wire protocol is not currently documented. To use it you must use the client library provided.
This is likely to change in a future release.
Registering Classes With RPC Kryo
---------------------------------
In the present implementation of the node we use Kryo to generate the *on the wire* representation of contracts states
or any other classes that form part of the RPC arguments or response. To avoid the RPC interface being wide open to all
classes on the classpath, Cordapps will currently have to register any classes or custom serialisers they require with Kryo
if they are not one of those registered by default in ``RPCKryo`` via the plugin architecture. See :doc:`creating-a-cordapp`.
This will require some familiarity with Kryo. An example is shown in :doc:`tutorial-clientrpc-api`.
.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin
customisation point will either go away completely or change.
.. _CordaRPCClient: api/net.corda.client/-corda-r-p-c-client/index.html
.. _CordaRPCOps: api/net.corda.node.services.messaging/-corda-r-p-c-ops/index.html

View File

@ -16,6 +16,7 @@ specific details of the implementation, but you can extend the server in the fol
2. Service plugins: Register your services (see below).
3. Web APIs: You may register your own endpoints under /api/ of the built-in web server.
4. Static web endpoints: You may register your own static serving directories for serving web content.
5. Registering your additional classes used in RPC.
Services
--------

View File

@ -1,5 +1,6 @@
package net.corda.docs
import com.esotericsoftware.kryo.Kryo
import net.corda.client.CordaRPCClient
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
@ -7,6 +8,7 @@ import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.USD
import net.corda.core.div
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
@ -135,4 +137,17 @@ fun generateTransactions(proxy: CordaRPCOps) {
}
}
}
// END 6
// END 6
// START 7
data class ExampleRPCValue(val foo: String)
class ExampleRPCCordaPluginRegistry : CordaPluginRegistry() {
override fun registerRPCKryoTypes(kryo: Kryo): Boolean {
// Add classes like this.
kryo.register(ExampleRPCValue::class.java)
// You should return true, otherwise your plugin will be ignored for registering classes with Kryo.
return true
}
}
// END 7

View File

@ -79,3 +79,19 @@ Now let's try to visualise the transaction graph. We will use a graph drawing li
:end-before: END 5
If we run the client with ``Visualise`` we should see a simple random graph being drawn as new transactions are being created.
Registering classes from your Cordapp with RPC Kryo
--------------------------------------------------
As described in :doc:`clientrpc`, you currently have to register any additional classes you add that are needed in RPC
requests or responses with the `Kryo` instance RPC uses. Here's an example of how you do this for an example class.
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
:language: kotlin
:start-after: START 7
:end-before: END 7
See more on plugins in :doc:`creating-a-cordapp`.
.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin
customisation point will either go away completely or change.

View File

@ -20,10 +20,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash
import net.corda.core.node.NodeInfo
import net.corda.core.node.PhysicalLocation
import net.corda.core.node.ServiceEntry
import net.corda.core.node.WorldCoordinate
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.serialization.*
@ -117,6 +114,14 @@ class PermissionException(msg: String) : RuntimeException(msg)
// This is annoying to write out, but will make it easier to formalise the wire protocol when the time comes,
// because we can see everything we're using in one place.
private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null) : Kryo() {
companion object {
private val pluginRegistries: List<CordaPluginRegistry> by lazy {
val unusedKryo = Kryo()
// Sorting required to give a stable ordering, as Kryo allocates integer tokens for each registered class.
ServiceLoader.load(CordaPluginRegistry::class.java).toList().filter { it.registerRPCKryoTypes(unusedKryo) }.sortedBy { it.javaClass.name }
}
}
init {
isRegistrationRequired = true
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
@ -215,6 +220,7 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
register(ProtocolHandle::class.java)
register(KryoException::class.java)
register(StringBuffer::class.java)
pluginRegistries.forEach { it.registerRPCKryoTypes(this) }
}
// Helper method, attempt to reduce boiler plate code