Joel upgrade docs (#3730)

* Instructions on how to perform H2 DB upgrades without starting the node.

* Formatting error. Adds required subsections in H2 docs.

* Merges duplicated information about H2 DB access.

* Drops script references for now.

* Clarifies upgrade notes.

* Better title.

* Inr

* Addresses review comments.

* Addresses review comments.
This commit is contained in:
Joel Dudley 2018-08-03 13:34:34 +01:00 committed by GitHub
parent ff298e17e1
commit 286dc7b77d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 204 additions and 207 deletions

View File

@ -1,20 +1,37 @@
Database access when running H2
===============================
When running a node using the H2 database, the node can be configured to expose its internal database over socket which
can be browsed using any tool that can use JDBC drivers.
The JDBC URL is printed during node startup to the log and will typically look like this:
``jdbc:h2:tcp://localhost:31339/node``
.. contents::
The username and password can be altered in the :doc:`corda-configuration-file` but default to username "sa" and a blank
password.
Configuring the username and password
-------------------------------------
Any database browsing tool that supports JDBC can be used, but if you have IntelliJ Ultimate edition then there is
a tool integrated with your IDE. Just open the database window and add an H2 data source with the above details.
You will now be able to browse the tables and row data within them.
The database (a file called ``persistence.mv.db``) is created when the node first starts up. By default, it has an
administrator user ``sa`` and a blank password. The node requires the user with administrator permissions in order to
creates tables upon the first startup or after deploying new CorDapps with their own tables. The database password is
required only when the H2 database is exposed on non-localhost address (which is disabled by default).
By default, the node's H2 database is not exposed. This behaviour can be overridden by specifying the full network
address (interface and port), using the new ``h2Settings`` syntax in the node configuration.
This username and password can be changed in node configuration:
.. sourcecode:: groovy
dataSourceProperties = {
dataSource.user = [USER]
dataSource.password = [PASSWORD]
}
Note that changing the user/password for the existing node in ``node.conf`` will not update them in the H2 database.
You need to log into the database first to create a new user or change a user's password.
Connecting via a socket on a running node
-----------------------------------------
Configuring the port
^^^^^^^^^^^^^^^^^^^^
Nodes backed by an H2 database will not expose this database by default. To configure the node to expose its internal
database over a socket which can be browsed using any tool that can use JDBC drivers, you must specify the full network
address (interface and port) using the ``h2Settings`` syntax in the node configuration.
The configuration below will restrict the H2 service to run on ``localhost``:
@ -32,8 +49,8 @@ If you want H2 to auto-select a port (mimicking the old ``h2Port`` behaviour), y
address: "localhost:0"
}
If remote access is required, the address can be changed to ``0.0.0.0``.
The node requires a database password to be set when the database is exposed on the network interface to listen on.
If remote access is required, the address can be changed to ``0.0.0.0`` to listen on all interfaces. A password must be
set for the database user before doing so.
.. sourcecode:: groovy
@ -44,5 +61,44 @@ The node requires a database password to be set when the database is exposed on
dataSource.password : "strongpassword"
}
The previous ``h2Port`` syntax is now deprecated. ``h2Port`` will continue to work but the database
will only be accessible on localhost.
.. note:: The previous ``h2Port`` syntax is now deprecated. ``h2Port`` will continue to work but the database will only
be accessible on localhost.
Connecting to the database
^^^^^^^^^^^^^^^^^^^^^^^^^^
The JDBC URL is printed during node startup to the log and will typically look like this:
``jdbc:h2:tcp://localhost:31339/node``
Any database browsing tool that supports JDBC can be used.
Connecting using the H2 Console
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Download the **last stable** `h2 platform-independent zip <http://www.h2database.com/html/download.html>`_, unzip the
zip, and navigate in a terminal window to the unzipped folder
* Change directories to the bin folder: ``cd h2/bin``
* Run the following command to open the h2 web console in a web browser tab:
* Unix: ``sh h2.sh``
* Windows: ``h2.bat``
* Paste the node's JDBC URL into the JDBC URL field and click ``Connect``, using the default username (``sa``) and no
password (unless configured otherwise)
You will be presented with a web interface that shows the contents of your node's storage and vault, and provides an
interface for you to query them using SQL.
.. _h2_relative_path:
Connecting directly to the node's ``persistence.mv.db`` file
------------------------------------------------------------
You can also use the H2 Console to connect directly to the node's ``persistence.mv.db`` file. Ensure the node is off
before doing so, as access to the database file requires exclusive access. If the node is still running, the H2 Console
will return the following error:
``Database may be already in use: null. Possible solutions: close all other connection(s); use the server mode [90020-196]``.
``jdbc:h2:~/path/to/file/persistence``

View File

@ -3,57 +3,7 @@ Node database
Default in-memory database
--------------------------
By default, nodes store their data in an H2 database.
The database (a file persistence.mv.db) is created at the first node startup with the administrator user 'sa' and a blank password.
The user name and password can be changed in node configuration:
.. sourcecode:: groovy
dataSourceProperties = {
dataSource.user = [USER]
dataSource.password = [PASSWORD]
}
Note, changing user/password for the existing node in node.conf will not update them in the H2 database,
you need to login to the database first to create new user or change the user password.
The database password is required only when the H2 database is exposed on non-localhost address (which is disabled by default).
The node requires the user with administrator permissions in order to creates tables upon the first startup
or after deplying new CorDapps with own tables.
You can connect directly to a running node's database to see its
stored states, transactions and attachments as follows:
* Enable the H2 database access in the node configuration using the following syntax:
.. sourcecode:: groovy
h2Settings {
address: "localhost:0"
}
* Download the **last stable** `h2 platform-independent zip <http://www.h2database.com/html/download.html>`_, unzip the zip, and
navigate in a terminal window to the unzipped folder
* Change directories to the bin folder: ``cd h2/bin``
* Run the following command to open the h2 web console in a web browser tab:
* Unix: ``sh h2.sh``
* Windows: ``h2.bat``
* Find the node's JDBC connection string. Each node outputs its connection string in the terminal
window as it starts up. In a terminal window where a node is running, look for the following string:
``Database connection URL is : jdbc:h2:tcp://10.18.0.150:56736/node``
* Paste this string into the JDBC URL field and click ``Connect``, using the default username (``sa``) and no password.
You will be presented with a web interface that shows the contents of your node's storage and vault, and provides an
interface for you to query them using SQL.
The default behaviour is to expose the H2 database on localhost. This can be overridden in the
node configuration using ``h2Settings.address`` and specifying the address of the network interface to listen on,
or simply using ``0.0.0.0:0`` to listen on all interfaces. The node requires a database password to be set when
the database is exposed on the network interface to listen on.
By default, nodes store their data in an H2 database. See :doc:`node-database-access-h2`.
PostgreSQL
----------

View File

@ -9,24 +9,27 @@ first public Beta (:ref:`Milestone 12 <changelog_m12>`), to :ref:`V1.0 <changelo
General rules
-------------
Always remember to update the version identifiers in your project gradle file:
* Always remember to update the version identifiers in your project gradle file:
.. sourcecode:: shell
ext.corda_release_version = '1.0.0'
ext.corda_gradle_plugins_version = '1.0.0'
ext.corda_release_version = 'x.y.0'
ext.corda_gradle_plugins_version = 'x.y.0'
It may be necessary to update the version of major dependencies:
* It may also be necessary to update the version of major dependencies:
.. sourcecode:: shell
ext.kotlin_version = '1.1.4'
ext.quasar_version = '0.7.9'
ext.kotlin_version = 'x.y.z'
ext.quasar_version = 'x.y.z'
Please consult the relevant release notes of the release in question. If not specified, you may assume the
versions you are currently using are still in force.
* Please consult the relevant release notes of the release in question. If not specified, you may assume the
versions you are currently using are still in force
* We also strongly recommend cross referencing with the :doc:`changelog` to confirm changes
We also strongly recommend cross referencing with the :doc:`changelog` to confirm changes.
* To run database upgrades against H2, you'll need to connect to the node's database without starting the node. You can
do this by connecting directly to the node's ``persistence.mv.db`` file. See :ref:`h2_relative_path`
UNRELEASED
----------

View File

@ -15,26 +15,8 @@ Upgrading a CorDapp (outside of platform version upgrades)
CorDapp versioning
------------------
The Corda platform does not mandate a version number on a per-CorDapp basis. Different elements of a CorDapp are
allowed to evolve separately:
* States
* Contracts
* Services
* Flows
* Utilities and library functions
* All, or a subset, of the above
Sometimes, however, a change to one element will require changes to other elements. For example, changing a shared data
structure may require flow changes that are not backwards-compatible.
Areas of consideration
----------------------
This document will consider the following types of versioning:
* Flow versioning
* State and contract versioning
* State and state schema versioning
* Serialisation of custom types
allowed to evolve separately. Sometimes, however, a change to one element will require changes to other elements. For
example, changing a shared data structure may require flow changes that are not backwards-compatible.
Flow versioning
---------------
@ -48,8 +30,8 @@ The ``version`` property, which defaults to 1, specifies the flow's version. Thi
whenever there is a release of a flow which has changes that are not backwards-compatible. A non-backwards compatible
change is one that changes the interface of the flow.
What defines the interface of a flow?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Defining a flow's interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The flow interface is defined by the sequence of ``send`` and ``receive`` calls between an ``InitiatingFlow`` and an
``InitiatedBy`` flow, including the types of the data sent and received. We can picture a flow's interface as follows:
@ -75,8 +57,8 @@ As long as both the ``InitiatingFlow`` and the ``InitiatedBy`` flows conform to
be implemented in any way you see fit (including adding proprietary business logic that is not shared with other
parties).
What constitutes a non-backwards compatible flow change?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Non-backwards compatible flow changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A flow can become backwards-incompatible in two main ways:
* The sequence of ``send`` and ``receive`` calls changes:
@ -86,8 +68,8 @@ A flow can become backwards-incompatible in two main ways:
* The types of the ``send`` and ``receive`` calls changes
What happens when running flows with incompatible versions?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consequences of running flows with incompatible versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pairs of ``InitiatingFlow`` flows and ``InitiatedBy`` flows that have incompatible interfaces are likely to exhibit the
following behaviour:
@ -98,24 +80,8 @@ following behaviour:
* One of the flows ends with an exception: "Counterparty flow terminated early on the other side", because one flow
sends some data to another flow, but the latter flow has already ended
How do I upgrade my flows?
~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Update the flow and test the changes. Increment the flow version number in the ``InitiatingFlow`` annotation.
2. Ensure that all versions of the existing flow have finished running and there are no pending ``SchedulableFlows`` on
any of the nodes on the business network. This can be done by *draining the node* (see below).
3. Shut down the node.
4. Replace the existing CorDapp JAR with the CorDapp JAR containing the new flow.
5. Start the node.
If you shut down all nodes and upgrade them all at the same time, any incompatible change can be made.
In situations where some nodes may still be using previous versions of a flow and thus new versions of your flow may
talk to old versions, the updated flows need to be backwards-compatible. This will be the case for almost any real
deployment in which you cannot easily coordinate the roll-out of new code across the network.
How do I ensure flow backwards-compatibility?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ensuring flow backwards-compatibility
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``InitiatingFlow`` version number is included in the flow session handshake and exposed to both parties via the
``FlowLogic.getFlowContext`` method. This method takes a ``Party`` and returns a ``FlowContext`` object which describes
the flow running on the other side. In particular, it has a ``flowVersion`` property which can be used to
@ -159,8 +125,8 @@ This code shows a flow that in its first version expected to receive an Int, but
expect a String. This flow is still able to communicate with parties that are running the older CorDapp containing
the older flow.
How do I deal with interface changes to inlined subflows?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Handling interface changes to inlined subflows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is an example of an in-lined subflow:
.. container:: codeset
@ -254,8 +220,26 @@ Flows which are not an ``InitiatingFlow`` or ``InitiatedBy`` flow, or inlined su
``InitiatingFlow`` or ``InitiatedBy`` flow, can be updated without consideration of backwards-compatibility. Flows of
this type include utility flows for querying the vault and flows for reaching out to external systems.
Flow drains
~~~~~~~~~~~
Performing flow upgrades
~~~~~~~~~~~~~~~~~~~~~~~~
1. Update the flow and test the changes. Increment the flow version number in the ``InitiatingFlow`` annotation
2. Ensure that all versions of the existing flow have finished running and there are no pending ``SchedulableFlows`` on
any of the nodes on the business network. This can be done by :ref:`draining_the_node`
3. Shut down the node
4. Replace the existing CorDapp JAR with the CorDapp JAR containing the new flow
5. Start the node
If you shut down all nodes and upgrade them all at the same time, any incompatible change can be made.
In situations where some nodes may still be using previous versions of a flow and thus new versions of your flow may
talk to old versions, the updated flows need to be backwards-compatible. This will be the case for almost any real
deployment in which you cannot easily coordinate the roll-out of new code across the network.
.. _draining_the_node:
Draining the node
~~~~~~~~~~~~~~~~~
A flow *checkpoint* is a serialised snapshot of the flow's stack frames and any objects reachable from the stack.
Checkpoints are saved to the database automatically when a flow suspends or resumes, which typically happens when
@ -280,34 +264,36 @@ Contract and state versioning
There are two types of contract/state upgrade:
1. *Implicit:* By allowing multiple implementations of the contract ahead of time, using constraints. See :doc:`api-contract-constraints` to learn more.
2. *Explicit:* By creating a special *contract upgrade transaction* and getting all participants of a state to sign it using the
contract upgrade flows.
1. *Implicit:* By allowing multiple implementations of the contract ahead of time, using constraints. See
:doc:`api-contract-constraints` to learn more
2. *Explicit:* By creating a special *contract upgrade transaction* and getting all participants of a state to sign it
using the contract upgrade flows
This section of the documentation focuses only on the *explicit* type of upgrade.
This section of the documentation focuses only on *explicit* upgrades.
In an explicit upgrade contracts and states can be changed in arbitrary ways, if and only if all of the state'
s participants agree to the proposed upgrade. The following combinations of upgrades are possible:
In an explicit upgrade, contracts and states can be changed in arbitrary ways, if and only if all of the state's
participants agree to the proposed upgrade. The following combinations of upgrades are possible:
* A contract is upgraded while the state definition remains the same.
* A state is upgraded while the contract stays the same.
* The state and the contract are updated simultaneously.
* A contract is upgraded while the state definition remains the same
* A state is upgraded while the contract stays the same
* The state and the contract are updated simultaneously
The procedure for updating a state or a contract using a flag-day approach is quite simple:
Performing contract and state upgrades
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Update and test the state or contract.
* Produce a new CorDapp JAR file and distribute it to all the relevant parties.
* Each node operator stops their node, replaces the existing JAR with the new one, and restarts. They may wish to do
a node drain first to avoid the definition of states or contracts changing whilst a flow is in progress.
* Run the contract upgrade authorisation flow for each state that requires updating on every node.
* For each state, one node should run the contract upgrade initiation flow, which will contact the rest.
1. Preserve the existing state and contract definitions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Currently, all nodes must **permanently** keep **all** old state and contract definitions on their node's classpath
Update Process
~~~~~~~~~~~~~~
.. note:: Once the contract-code-as-an-attachment feature has been implemented, nodes will only be required to keep the
old state and contract definitions on their node's classpath for the duration of the upgrade
Writing the new state and contract definitions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Start by updating the contract and/or state definitions. There are no restrictions on how states are updated. However,
Changing a state or contract's package constitutes a definition change. If you want to move a state or contract
definition to a new package, you must also preserve the definition in the old package
2. Write the new state and contract definitions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Update the contract and/or state definitions. There are no restrictions on how states are updated. However,
upgraded contracts must implement the ``UpgradedContract`` interface. This interface is defined as:
.. sourcecode:: kotlin
@ -337,18 +323,29 @@ For example, in case of hash constraints the hash of the legacy JAR file should
override val legacyContractConstraint: AttachmentConstraint
get() = HashAttachmentConstraint(SecureHash.parse("E02BD2B9B010BBCE49C0D7C35BECEF2C79BEB2EE80D902B54CC9231418A4FA0C"))
Authorising the upgrade
^^^^^^^^^^^^^^^^^^^^^^^
Once the new states and contracts are on the classpath for all the relevant nodes, the next step is for all nodes to
run the ``ContractUpgradeFlow.Authorise`` flow. This flow takes a ``StateAndRef`` of the state to update as well as a
reference to the new contract, which must implement the ``UpgradedContract`` interface.
3. Create the new CorDapp JAR
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Produce a new CorDapp JAR file and distribute it to all the relevant nodes. This JAR file should contain all the old
contract and state definitions, plus any new contract and state definitions.
4. Restart the nodes
^^^^^^^^^^^^^^^^^^^^
Have each node operator stop their node, replace the existing CorDapp JAR with the new CorDapp JAR, and restart their
node. They may wish to do a :ref:`node drain <draining_the_node>` first to avoid the definition of states or contracts
changing whilst a flow is in progress.
5. Authorise the upgrade
^^^^^^^^^^^^^^^^^^^^^^^^
Once the new states and contracts are on the classpath for all the relevant nodes, the nodes must all run the
``ContractUpgradeFlow.Authorise`` flow. This flow takes a ``StateAndRef`` of the state to update as well as a reference
to the new contract, which must implement the ``UpgradedContract`` interface.
At any point, a node administrator may de-authorise a contract upgrade by running the
``ContractUpgradeFlow.Deauthorise`` flow.
Performing the upgrade
6. Perform the upgrade
^^^^^^^^^^^^^^^^^^^^^^
Once all nodes have performed the authorisation process, a participant must be chosen to initiate the upgrade via the
Once all nodes have performed the authorisation process, a **single** node must initiate the upgrade via the
``ContractUpgradeFlow.Initiate`` flow for each state object. This flow has the following signature:
.. sourcecode:: kotlin
@ -375,9 +372,6 @@ Capabilities of the contract upgrade flows
* Equally, the state doesn't have to change at all
* If a node has not yet run the contract upgrade authorisation flow, they will not be able to upgrade the contract
and/or state objects
* Upgrade authorisations can subsequently be deauthorised
* Upgrades do not have to happen immediately. For a period, the two parties can use the old states and contracts
side-by-side
* State schema changes are handled separately
Writing new states and contracts
@ -390,64 +384,19 @@ Writing new states and contracts
value
* Updated state objects can use the old contract code as long as there is no requirement to update it
Dealing with old contract code JAR files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Currently, all parties **must** keep the old state and contract definitions on their node's classpath as they will
always be required to verify transactions involving previous versions of the state using previous versions of the
contract
* This will change when the contract code as an attachment feature has been fully implemented.
Permissioning
^^^^^^^^^^^^^
* Only node administrators are able to run the contract upgrade authorisation and deauthorisation flows
Logistics
^^^^^^^^^
* All nodes need to run the contract upgrade authorisation flow
* All nodes need to run the contract upgrade authorisation flow to upgrade the contract and/or state objects
* Only node administrators are able to run the contract upgrade authorisation and deauthorisation flows
* Upgrade authorisations can subsequently be deauthorised
* Only one node should run the contract upgrade initiation flow. If multiple nodes run it for the same ``StateRef``, a
double-spend will occur for all but the first completed upgrade
* Upgrades do not have to happen immediately. For a period, the two parties can use the old states and contracts
side-by-side
* The supplied upgrade flows upgrade one state object at a time
Serialisation
-------------
Currently, the serialisation format for everything except flow checkpoints (which uses a Kryo-based format) is based
upon AMQP 1.0, a self-describing and controllable serialisation format. AMQP is desirable because it allows us to have
a schema describing what has been serialized alongside the data itself. This assists with versioning and deserialising
long-ago archived data, among other things.
Writing classes
~~~~~~~~~~~~~~~
Although not strictly related to versioning, AMQP serialisation dictates that we must write our classes in a particular way:
* Your class must have a constructor that takes all the properties that you wish to record in the serialized form. This
is required in order for the serialization framework to reconstruct an instance of your class
* If more than one constructor is provided, the serialization framework needs to know which one to use. The
``@ConstructorForDeserialization`` annotation can be used to indicate the chosen constructor. For a Kotlin class
without the ``@ConstructorForDeserialization`` annotation, the primary constructor is selected
* The class must be compiled with parameter names in the .class file. This is the default in Kotlin but must be turned
on in Java (using the ``-parameters`` command line option to ``javac``)
* Your class must provide a Java Bean getter for each of the properties in the constructor, with a matching name. For
example, if a class has the constructor parameter ``foo``, there must be a getter called ``getFoo()``. If ``foo`` is
a boolean, the getter may optionally be called ``isFoo()``. This is why the class must be compiled with parameter
names turned on
* The class must be annotated with ``@CordaSerializable``
* The declared types of constructor arguments/getters must be supported, and where generics are used the generic
parameter must be a supported type, an open wildcard (*), or a bounded wildcard which is currently widened to an open
wildcard
* Any superclass must adhere to the same rules, but can be abstract
* Object graph cycles are not supported, so an object cannot refer to itself, directly or indirectly
Writing enums
~~~~~~~~~~~~~
Elements cannot be added to enums in a new version of the code. Hence, enums are only a good fit for genuinely static
data that will never change (e.g. days of the week). A ``Buy`` or ``Sell`` flag is another. However, something like
``Trade Type`` or ``Currency Code`` will likely change. For those, it is preferable to choose another representation,
such as a string.
State schemas
-------------
State schema versioning
-----------------------
By default, all state objects are serialised to the database as a string of bytes and referenced by their ``StateRef``.
However, it is also possible to define custom schemas for serialising particular properties or combinations of
properties, so that they can be queried from a source other than the Corda Vault. This is done by implementing the
@ -638,4 +587,43 @@ Then, in ``generateMappedObject``, add support for the new schema:
}
With this approach, whenever the state object is stored in the vault, a representation of it will be stored in two
separate database tables where possible - one for each supported schema.
separate database tables where possible - one for each supported schema.
Serialisation
-------------
The Corda serialisation format
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Currently, the serialisation format for everything except flow checkpoints (which uses a Kryo-based format) is based
on AMQP 1.0, a self-describing and controllable serialisation format. AMQP is desirable because it allows us to have
a schema describing what has been serialized alongside the data itself. This assists with versioning and deserialising
long-ago archived data, among other things.
Writing classes that meet the serialisation format requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Although not strictly related to versioning, AMQP serialisation dictates that we must write our classes in a particular way:
* Your class must have a constructor that takes all the properties that you wish to record in the serialized form. This
is required in order for the serialization framework to reconstruct an instance of your class
* If more than one constructor is provided, the serialization framework needs to know which one to use. The
``@ConstructorForDeserialization`` annotation can be used to indicate the chosen constructor. For a Kotlin class
without the ``@ConstructorForDeserialization`` annotation, the primary constructor is selected
* The class must be compiled with parameter names in the .class file. This is the default in Kotlin but must be turned
on in Java (using the ``-parameters`` command line option to ``javac``)
* Your class must provide a Java Bean getter for each of the properties in the constructor, with a matching name. For
example, if a class has the constructor parameter ``foo``, there must be a getter called ``getFoo()``. If ``foo`` is
a boolean, the getter may optionally be called ``isFoo()``. This is why the class must be compiled with parameter
names turned on
* The class must be annotated with ``@CordaSerializable``
* The declared types of constructor arguments/getters must be supported, and where generics are used the generic
parameter must be a supported type, an open wildcard (*), or a bounded wildcard which is currently widened to an open
wildcard
* Any superclass must adhere to the same rules, but can be abstract
* Object graph cycles are not supported, so an object cannot refer to itself, directly or indirectly
Writing enums
~~~~~~~~~~~~~
Elements cannot be added to enums in a new version of the code. Hence, enums are only a good fit for genuinely static
data that will never change (e.g. days of the week). A ``Buy`` or ``Sell`` flag is another. However, something like
``Trade Type`` or ``Currency Code`` will likely change. For those, it is preferable to choose another representation,
such as a string.