Skip to content

Commit 701d842

Browse files
author
Katelyn Baker
authored
CORDA-1640 - Proxy serializer documentation (corda#3389)
1 parent 999ee49 commit 701d842

File tree

1 file changed

+77
-7
lines changed

1 file changed

+77
-7
lines changed

docs/source/cordapp-custom-serializers.rst

+77-7
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,26 @@ Serializers must
2121
* Inherit from ``net.corda.core.serialization.SerializationCustomSerializer``
2222
* Provide a proxy class to transform the object to and from
2323
* Implement the ``toProxy`` and ``fromProxy`` methods
24-
* Be either included into CorDapp Jar or made known to the running process via ``amqp.custom.serialization.scanSpec``
25-
system property. This system property may be necessary to be able to discover custom serializer in the classpath. At a minimum the value
26-
of the property should include comma separated set of packages where custom serializers located. Full syntax includes
27-
scanning specification as defined by: `<http://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#scan-spec>`
24+
* Be either included into the CorDapp Jar or made known to the running process via the ``amqp.custom.serialization.scanSpec``
25+
system property. This system property may be necessary to be able to discover custom serializer in the classpath.
26+
At a minimum the value of the property should include comma separated set of packages where custom serializers located.
27+
Full syntax includes scanning specification as defined by: `<http://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#scan-spec>`
2828

2929
Serializers inheriting from ``SerializationCustomSerializer`` have to implement two methods and two types.
3030

3131
Example
3232
-------
33-
Consider this example class:
33+
Consider the following class:
3434

3535
.. sourcecode:: java
3636

3737
public final class Example {
3838
private final Int a
3939
private final Int b
4040

41+
// Because this is marked private the serialization framework will not
42+
// consider it when looking to see which constructor should be used
43+
// when serializing instances of this class.
4144
private Example(Int a, Int b) {
4245
this.a = a;
4346
this.b = b;
@@ -52,23 +55,90 @@ Consider this example class:
5255
Without a custom serializer we cannot serialize this class as there is no public constructor that facilitates the
5356
initialisation of all of its properties.
5457

55-
To be serializable by Corda this would require a custom serializer as follows:
58+
.. note:: This is clearly a contrived example, simply making the constructor public would alleviate the issues.
59+
However, for the purposes of this example we are assuming that for external reasons this cannot be done.
60+
61+
To be serializable by Corda this would require a custom serializer to be written that can transform the unserializable
62+
class into a form we can serialize. Continuing the above example, this could be written as follows:
5663

5764
.. sourcecode:: kotlin
5865

5966
class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
67+
/**
68+
* This is the actual proxy class that is used as an intermediate representation
69+
* of the Example class
70+
*/
6071
data class Proxy(val a: Int, val b: Int)
6172
73+
/**
74+
* This method should be able to take an instance of the type being proxied and
75+
* transpose it into that form, instantiating an instance of the Proxy object (it
76+
* is this class instance that will be serialized into the byte stream.
77+
*/
6278
override fun toProxy(obj: Example) = Proxy(obj.a, obj.b)
6379
80+
/**
81+
* This method is used during deserialization. The bytes will have been read
82+
* from the serialized blob and an instance of the Proxy class returned, we must
83+
* now be able to transform that back into an instance of our original class.
84+
*
85+
* In our example this requires us to evoke the static *of* method on the
86+
* Example class, transforming the serialized properties of the Proxy instance
87+
* into a form expected by the construction method of Example.
88+
*/
6489
override fun fromProxy(proxy: Proxy) : Example {
6590
val constructorArg = IntArray(2);
6691
constructorArg[0] = proxy.a
6792
constructorArg[1] = proxy.b
68-
return Example.create(constructorArg)
93+
return Example.of(constructorArg)
6994
}
7095
}
7196
97+
In the above ``ExampleSerializer`` is the actual serializer that will be loaded by the framework to
98+
serialize instances of the ``Example`` type.
99+
100+
``ExampleSerializer.Proxy`` is the intermediate representation used by the framework to represent
101+
instances of ``Example`` within the wire format.
102+
103+
The Proxy Object
104+
----------------
105+
106+
The proxy object should be thought of as an intermediate representation that the serialization framework
107+
can reason about. One is being written for a class because, for some reason, that class cannot be
108+
introspected successfully but that framework. It is therefore important to note that the proxy class must
109+
only contain elements that the framework can reason about.
110+
111+
The proxy class itself is distinct from the proxy serializer. The serializer must refer to the unserializable
112+
type in the ``toProxy`` and ``fromProxy`` methods.
113+
114+
For example, the first thought a developer may have when implementing a proxy class is to simply *wrap* an
115+
instance of the object being proxied. This is shown below
116+
117+
.. sourcecode:: kotlin
118+
119+
class ExampleSerializer : SerializationCustomSerializer<Example, ExampleSerializer.Proxy> {
120+
/**
121+
* In this example, we are trying to wrap the Example type to make it serializable
122+
*/
123+
data class Proxy(val e: Example)
124+
125+
override fun toProxy(obj: Example) = Proxy(obj)
126+
127+
override fun fromProxy(proxy: Proxy) : Example {
128+
return proxy.e
129+
}
130+
}
131+
132+
However, this will not work because what we've created is a recursive loop whereby synthesising a serializer
133+
for the ``Example`` type requires synthesising one for ``ExampleSerializer.Proxy``. However, that requires
134+
one for ``Example`` and so on and so forth until we get a ``StackOverflowException``.
135+
136+
The solution, as shown initially, is to create the intermediate form (the Proxy object) purely in terms
137+
the serialization framework can reason about.
138+
139+
.. important:: When composing a proxy object for a class be aware that everything within that structure will be written
140+
into the serialized byte stream.
141+
72142
Whitelisting
73143
------------
74144
By writing a custom serializer for a class it has the effect of adding that class to the whitelist, meaning such

0 commit comments

Comments
 (0)