mirror of
https://github.com/corda/corda.git
synced 2025-06-23 01:19:00 +00:00
Merge remote-tracking branch 'open/master' into os-merge-2018-06-18-17_42
This commit is contained in:
@ -1,3 +1,9 @@
|
||||
.. highlight:: kotlin
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/codesets.js"></script>
|
||||
|
||||
Pluggable Serializers for CorDapps
|
||||
==================================
|
||||
|
||||
@ -21,16 +27,16 @@ Serializers must
|
||||
* Inherit from ``net.corda.core.serialization.SerializationCustomSerializer``
|
||||
* Provide a proxy class to transform the object to and from
|
||||
* Implement the ``toProxy`` and ``fromProxy`` methods
|
||||
* Be either included into CorDapp Jar or made known to the running process via ``amqp.custom.serialization.scanSpec``
|
||||
system property. This system property may be necessary to be able to discover custom serializer in the classpath. At a minimum the value
|
||||
of the property should include comma separated set of packages where custom serializers located. Full syntax includes
|
||||
scanning specification as defined by: `<http://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#scan-spec>`
|
||||
* Be either included into the CorDapp Jar or made known to the running process via the ``amqp.custom.serialization.scanSpec``
|
||||
system property. This system property may be necessary to be able to discover custom serializer in the classpath.
|
||||
At a minimum the value of the property should include comma separated set of packages where custom serializers located.
|
||||
Full syntax includes scanning specification as defined by: `<http://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#scan-spec>`
|
||||
|
||||
Serializers inheriting from ``SerializationCustomSerializer`` have to implement two methods and two types.
|
||||
|
||||
Example
|
||||
-------
|
||||
Consider this example class:
|
||||
Consider the following class:
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
@ -38,6 +44,9 @@ Consider this example class:
|
||||
private final Int a
|
||||
private final Int b
|
||||
|
||||
// Because this is marked private the serialization framework will not
|
||||
// consider it when looking to see which constructor should be used
|
||||
// when serializing instances of this class.
|
||||
private Example(Int a, Int b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
@ -52,23 +61,166 @@ Consider this example class:
|
||||
Without a custom serializer we cannot serialize this class as there is no public constructor that facilitates the
|
||||
initialisation of all of its properties.
|
||||
|
||||
To be serializable by Corda this would require a custom serializer as follows:
|
||||
.. note:: This is clearly a contrived example, simply making the constructor public would alleviate the issues.
|
||||
However, for the purposes of this example we are assuming that for external reasons this cannot be done.
|
||||
|
||||
To be serializable by Corda this would require a custom serializer to be written that can transform the unserializable
|
||||
class into a form we can serialize. Continuing the above example, this could be written as follows:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
/**
|
||||
* The class lacks a public constructor that takes parameters it can associate
|
||||
* with its properties and is thus not serializable by the CORDA serialization
|
||||
* framework.
|
||||
*/
|
||||
class Example {
|
||||
private int a;
|
||||
private int b;
|
||||
|
||||
public int getA() { return a; }
|
||||
public int getB() { return b; }
|
||||
|
||||
public Example(List<int> l) {
|
||||
this.a = l.get(0);
|
||||
this.b = l.get(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the class that will Proxy instances of Example within the serializer
|
||||
*/
|
||||
public class ExampleProxy {
|
||||
/**
|
||||
* These properties will be serialized into the byte stream, this is where we choose how to
|
||||
* represent instances of the object we're proxying. In this example, which is somewhat
|
||||
* contrived, this choice is obvious. In your own classes / 3rd party libraries, however, this
|
||||
* may require more thought.
|
||||
*/
|
||||
private int proxiedA;
|
||||
private int proxiedB;
|
||||
|
||||
/**
|
||||
* The proxy class itself must be serializable by the framework, it must thus have a constructor that
|
||||
* can be mapped to the properties of the class via getter methods.
|
||||
*/
|
||||
public int getProxiedA() { return proxiedA; }
|
||||
public int getProxiedB() { return proxiedB; }
|
||||
|
||||
public ExampleProxy(int proxiedA, int proxiedB) {
|
||||
this.proxiedA = proxiedA;
|
||||
this.proxiedB = proxiedB;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finally this is the custom serializer that will automatically loaded into the serialization
|
||||
* framework when the CorDapp Jar is scanned at runtime.
|
||||
*/
|
||||
public class ExampleSerializer implements SerializationCustomSerializer<Example, ExampleProxy> {
|
||||
|
||||
/**
|
||||
* Given an instance of the Example class, create an instance of the proxying object ExampleProxy.
|
||||
*
|
||||
* Essentially convert Example -> ExampleProxy
|
||||
*/
|
||||
public ExampleProxy toProxy(Example obj) {
|
||||
return new ExampleProxy(obj.getA(), obj.getB());
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversely, given an instance of the proxy object, revert that back to an instance of the
|
||||
* type being proxied.
|
||||
*
|
||||
* Essentially convert ExampleProxy -> Example
|
||||
*/
|
||||
public Example fromProxy(ExampleProxy proxy) {
|
||||
List<int> l = new ArrayList<int>(2);
|
||||
l.add(proxy.getProxiedA());
|
||||
l.add(proxy.getProxiedB());
|
||||
return new Example(l);
|
||||
}
|
||||
}
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
|
||||
/**
|
||||
* This is the actual proxy class that is used as an intermediate representation
|
||||
* of the Example class
|
||||
*/
|
||||
data class Proxy(val a: Int, val b: Int)
|
||||
|
||||
/**
|
||||
* This method should be able to take an instance of the type being proxied and
|
||||
* transpose it into that form, instantiating an instance of the Proxy object (it
|
||||
* is this class instance that will be serialized into the byte stream.
|
||||
*/
|
||||
override fun toProxy(obj: Example) = Proxy(obj.a, obj.b)
|
||||
|
||||
/**
|
||||
* This method is used during deserialization. The bytes will have been read
|
||||
* from the serialized blob and an instance of the Proxy class returned, we must
|
||||
* now be able to transform that back into an instance of our original class.
|
||||
*
|
||||
* In our example this requires us to evoke the static "of" method on the
|
||||
* Example class, transforming the serialized properties of the Proxy instance
|
||||
* into a form expected by the construction method of Example.
|
||||
*/
|
||||
override fun fromProxy(proxy: Proxy) : Example {
|
||||
val constructorArg = IntArray(2);
|
||||
constructorArg[0] = proxy.a
|
||||
constructorArg[1] = proxy.b
|
||||
return Example.of(constructorArg)
|
||||
}
|
||||
}
|
||||
|
||||
In the above examples
|
||||
|
||||
- ``ExampleSerializer`` is the actual serializer that will be loaded by the framework to serialize instances of the ``Example`` type.
|
||||
- ``ExampleSerializer.Proxy``, in the Kotlin example, and ``ExampleProxy`` in the Java example, is the intermediate representation used by the framework to represent instances of ``Example`` within the wire format.
|
||||
|
||||
The Proxy Object
|
||||
----------------
|
||||
|
||||
The proxy object should be thought of as an intermediate representation that the serialization framework
|
||||
can reason about. One is being written for a class because, for some reason, that class cannot be
|
||||
introspected successfully but that framework. It is therefore important to note that the proxy class must
|
||||
only contain elements that the framework can reason about.
|
||||
|
||||
The proxy class itself is distinct from the proxy serializer. The serializer must refer to the unserializable
|
||||
type in the ``toProxy`` and ``fromProxy`` methods.
|
||||
|
||||
For example, the first thought a developer may have when implementing a proxy class is to simply *wrap* an
|
||||
instance of the object being proxied. This is shown below
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
|
||||
data class Proxy(val a: Int, val b: Int)
|
||||
/**
|
||||
* In this example, we are trying to wrap the Example type to make it serializable
|
||||
*/
|
||||
data class Proxy(val e: Example)
|
||||
|
||||
override fun toProxy(obj: Example) = Proxy(obj.a, obj.b)
|
||||
override fun toProxy(obj: Example) = Proxy(obj)
|
||||
|
||||
override fun fromProxy(proxy: Proxy) : Example {
|
||||
val constructorArg = IntArray(2);
|
||||
constructorArg[0] = proxy.a
|
||||
constructorArg[1] = proxy.b
|
||||
return Example.create(constructorArg)
|
||||
return proxy.e
|
||||
}
|
||||
}
|
||||
|
||||
However, this will not work because what we've created is a recursive loop whereby synthesising a serializer
|
||||
for the ``Example`` type requires synthesising one for ``ExampleSerializer.Proxy``. However, that requires
|
||||
one for ``Example`` and so on and so forth until we get a ``StackOverflowException``.
|
||||
|
||||
The solution, as shown initially, is to create the intermediate form (the Proxy object) purely in terms
|
||||
the serialization framework can reason about.
|
||||
|
||||
.. important:: When composing a proxy object for a class be aware that everything within that structure will be written
|
||||
into the serialized byte stream.
|
||||
|
||||
Whitelisting
|
||||
------------
|
||||
By writing a custom serializer for a class it has the effect of adding that class to the whitelist, meaning such
|
||||
|
@ -1,3 +1,9 @@
|
||||
.. raw:: html
|
||||
|
||||
<style> .red {color:red} </style>
|
||||
|
||||
.. role:: red
|
||||
|
||||
Deterministic Corda Modules
|
||||
===========================
|
||||
|
||||
@ -86,6 +92,103 @@ The build generates each of Corda's deterministic JARs in six steps:
|
||||
This step will fail if ProGuard spots any Java API references that still cannot be satisfied by the deterministic
|
||||
``rt.jar``, and hence it will break the build.
|
||||
|
||||
Configuring IntelliJ with a Deterministic SDK
|
||||
---------------------------------------------
|
||||
|
||||
We would like to configure IntelliJ so that it will highlight uses of non-deterministic Java APIs as :red:`not found`.
|
||||
Or, more specifically, we would like IntelliJ to use the ``deterministic-rt.jar`` as a "Module SDK" for deterministic
|
||||
modules rather than the ``rt.jar`` from the default project SDK, to make IntelliJ consistent with Gradle.
|
||||
|
||||
This is possible, but slightly tricky to configure because IntelliJ will not recognise an SDK containing only the
|
||||
``deterministic-rt.jar`` as being valid. It also requires that IntelliJ delegate all build tasks to Gradle, and that
|
||||
Gradle be configured to use the Project's SDK.
|
||||
|
||||
Creating the Deterministic SDK
|
||||
#. Create a JDK Home directory with the following contents:
|
||||
|
||||
``jre/lib/rt.jar``
|
||||
|
||||
where ``rt.jar`` here is this renamed artifact:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<dependency>
|
||||
<groupId>net.corda</groupId>
|
||||
<artifactId>deterministic-rt</artifactId>
|
||||
<classifier>api</classifier>
|
||||
</dependency>
|
||||
|
||||
..
|
||||
|
||||
.. note:: Gradle already creates this JDK in the project's ``jdk8u-deterministic/jdk`` directory, and you could
|
||||
configure IntelliJ to use this location as well. However, you should also be aware that IntelliJ SDKs
|
||||
are available for *all* projects to use.
|
||||
|
||||
To create this deterministic JDK image, execute the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ gradlew jdk8u-deterministic:copyJdk
|
||||
|
||||
..
|
||||
|
||||
#. While IntelliJ is *not* running, locate the ``config/options/jdk.table.xml`` file in IntelliJ's configuration
|
||||
directory. Add an empty ``<jdk>`` section to this file:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<jdk version="2">
|
||||
<name value="1.8 (Deterministic)"/>
|
||||
<type value="JavaSDK"/>
|
||||
<version value="java version "1.8.0""/>
|
||||
<homePath value=".. path to the deterministic JDK directory .."/>
|
||||
<roots>
|
||||
</roots>
|
||||
</jdk>
|
||||
|
||||
..
|
||||
|
||||
#. Open IntelliJ and select ``File/Project Structure/Platform Settings/SDKs``. The "1.8 (Deterministic)" SDK should
|
||||
now be present. Select it and then click on the ``Classpath`` tab. Press the "Add" / "Plus" button to add
|
||||
``rt.jar`` to the SDK's classpath. Then select the ``Annotations`` tab and include the same JAR(s) as the other
|
||||
SDKs.
|
||||
|
||||
Configuring the Corda Project
|
||||
#. Open the root ``build.gradle`` file and define this property:
|
||||
|
||||
.. code-block:: gradle
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
...
|
||||
deterministic_idea_sdk = '1.8 (Deterministic)'
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
..
|
||||
|
||||
Configuring IntelliJ
|
||||
#. Go to ``File/Settings/Build, Execution, Deployment/Build Tools/Gradle``, and configure Gradle's JVM to be the
|
||||
project's JVM.
|
||||
|
||||
#. Go to ``File/Settings/Build, Execution, Deployment/Build Tools/Gradle/Runner``, and select these options:
|
||||
|
||||
- Delegate IDE build/run action to Gradle
|
||||
- Run tests using the Gradle Test Runner
|
||||
|
||||
#. Delete all of the ``out`` directories that IntelliJ has previously generated for each module.
|
||||
|
||||
#. Go to ``View/Tool Windows/Gradle`` and click the ``Refresh all Gradle projects`` button.
|
||||
|
||||
These steps will enable IntelliJ's presentation compiler to use the deterministic ``rt.jar`` with the following modules:
|
||||
|
||||
- ``core-deterministic``
|
||||
- ``serialization-deterministic``
|
||||
- ``core-deterministic:testing:common``
|
||||
|
||||
but still build everything using Gradle with the full JDK.
|
||||
|
||||
Testing the Deterministic Modules
|
||||
---------------------------------
|
||||
|
||||
@ -108,7 +211,7 @@ The ``testing`` module also has two sub-modules:
|
||||
.. _deterministic_annotations:
|
||||
|
||||
Applying @KeepForDJVM and @DeleteForDJVM annotations
|
||||
---------------------------------------------------------
|
||||
----------------------------------------------------
|
||||
|
||||
Corda developers need to understand how to annotate classes in the ``core`` and ``serialization`` modules correctly
|
||||
in order to maintain the deterministic JARs.
|
||||
|
@ -1,65 +0,0 @@
|
||||
Network Simulator
|
||||
=================
|
||||
|
||||
A network simulator is provided which shows traffic between nodes through the lifecycle of an interest rate swap
|
||||
contract. It can optionally also show network setup, during which nodes register themselves with the network
|
||||
map service and are notified of the changes to the map. The network simulator is run from the command line via Gradle:
|
||||
|
||||
**Windows**::
|
||||
|
||||
gradlew.bat :samples:network-visualiser:run
|
||||
|
||||
**Other**::
|
||||
|
||||
./gradlew :samples:network-visualiser:run
|
||||
|
||||
You can produce a standalone JAR of the tool by using the ``:samples:network-visualiser:deployVisualiser`` target
|
||||
and then using the ``samples/network-visualiser/build/libs/network-visualiser-*-capsule.jar`` file, where * is
|
||||
whatever the current Corda version is.
|
||||
|
||||
What it is and is not
|
||||
---------------------
|
||||
|
||||
The simulator currently exists as an illustrative tool to help with explaining how Corda works in an example scenario.
|
||||
It utilises the ``Simulator`` tools that support creating a simulated Corda network and the nodes running in it within
|
||||
a single JVM, as an extension of the ``MockNetwork`` testing framework. See more about the ``MockNetwork`` and
|
||||
testing flows here: :doc:`flow-testing`.
|
||||
|
||||
Whilst it is not yet fully generic or full featured, the intention is for the simulator to mature into the following,
|
||||
which it presently cannot do without writing more code:
|
||||
|
||||
1. A tool for visualising new CorDapps and their flows to help with debugging, presentations, explanations and tutorials,
|
||||
but still running as a simulation in a single JVM.
|
||||
2. A tool to visualise the activity on a real Corda network deployment, with activity streams being fed from each node
|
||||
running in its own JVM, most likely on remote hosts.
|
||||
|
||||
Both of these scenarios would be fed by the standard observables in the RPC framework, rather than the local binding
|
||||
that the simulator uses currently. The ability to step through a flow one step at a time would obviously be restricted
|
||||
to single JVM simulations.
|
||||
|
||||
Interface
|
||||
---------
|
||||
|
||||
.. image:: resources/network-simulator.png
|
||||
|
||||
The network simulator can be run automatically, or stepped manually through each step of the interest rate swap. The
|
||||
options on the simulator window are:
|
||||
|
||||
Simulate initialisation
|
||||
If checked, the nodes registering with the network map is shown. Normally this setup step
|
||||
is not shown, but may be of interest to understand the details of node discovery.
|
||||
Run
|
||||
Runs the network simulation in automatic mode, in which it progresses each step on a timed basis. Once running,
|
||||
the simulation can be paused in order to manually progress it, or reset.
|
||||
Next
|
||||
Manually progress the simulation to the next step.
|
||||
Reset
|
||||
Reset the simulation (only available when paused).
|
||||
Map/Circle
|
||||
How the nodes are shown, by default nodes are rendered on a world map, but alternatively they can rendered
|
||||
in a circle layout.
|
||||
|
||||
While the simulation runs, details of the steps currently being executed are shown in a sidebar on the left hand side
|
||||
of the window.
|
||||
|
||||
.. TODO: Add documentation on how to use with different contracts for testing/debugging
|
@ -5,6 +5,5 @@ Tools
|
||||
:maxdepth: 1
|
||||
|
||||
blob-inspector
|
||||
network-simulator
|
||||
demobench
|
||||
node-explorer
|
||||
|
Reference in New Issue
Block a user