Merge pull request #552 from corda/shams-nms-version-check

Introducing Platform Version and its use by the NMS for min version r…
This commit is contained in:
Shams Asari 2017-04-24 16:30:40 +01:00 committed by GitHub
commit af7f5ef0d7
35 changed files with 247 additions and 237 deletions

View File

@ -4,7 +4,10 @@ buildscript {
file("$projectDir/constants.properties").withInputStream { constants.load(it) } file("$projectDir/constants.properties").withInputStream { constants.load(it) }
// Our version: bump this on release. // Our version: bump this on release.
ext.corda_version = "0.11-SNAPSHOT" ext.corda_release_version = "0.11-SNAPSHOT"
// Increment this on any release that changes public APIs anywhere in the Corda platform
// TODO This is going to be difficult until we have a clear separation throughout the code of what is public and what is internal
ext.corda_platform_version = 1
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
// Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things. // Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things.
@ -105,8 +108,17 @@ allprojects {
} }
} }
tasks.withType(Jar) { // Includes War and Ear
manifest {
attributes('Corda-Release-Version': corda_release_version)
attributes('Corda-Platform-Version': corda_platform_version)
attributes('Corda-Revision': corda_revision)
attributes('Corda-Vendor': 'Corda Open Source')
}
}
group 'net.corda' group 'net.corda'
version "$corda_version" version "$corda_release_version"
repositories { repositories {
mavenLocal() mavenLocal()

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=0.11.0 gradlePluginsVersion=0.11.1
kotlinVersion=1.1.1 kotlinVersion=1.1.1
guavaVersion=21.0 guavaVersion=21.0
typesafeConfigVersion=1.3.1 typesafeConfigVersion=1.3.1

View File

@ -50,6 +50,12 @@ sealed class StateMachineUpdate {
// TODO: The use of Pairs throughout is unfriendly for Java interop. // TODO: The use of Pairs throughout is unfriendly for Java interop.
interface CordaRPCOps : RPCOps { interface CordaRPCOps : RPCOps {
/**
* Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
* to be present.
*/
override val protocolVersion: Int get() = nodeIdentity().platformVersion
/** /**
* Returns a pair of currently in-progress state machine infos and an observable of future state machine adds/removes. * Returns a pair of currently in-progress state machine infos and an observable of future state machine adds/removes.
*/ */

View File

@ -19,7 +19,7 @@ data class ServiceEntry(val info: ServiceInfo, val identity: Party)
@CordaSerializable @CordaSerializable
data class NodeInfo(val address: SingleMessageRecipient, data class NodeInfo(val address: SingleMessageRecipient,
val legalIdentity: Party, val legalIdentity: Party,
val version: Version, val platformVersion: Int,
var advertisedServices: List<ServiceEntry> = emptyList(), var advertisedServices: List<ServiceEntry> = emptyList(),
val physicalLocation: PhysicalLocation? = null) { val physicalLocation: PhysicalLocation? = null) {
init { init {

View File

@ -1,35 +0,0 @@
package net.corda.core.node
import net.corda.core.serialization.CordaSerializable
import java.util.regex.Pattern
/**
* Versions of the same [major] version but with different [minor] versions are considered compatible with each other. One
* exception to this is when the major version is 0 - each different minor version should be considered incompatible.
*
* If two [Version]s are equal (i.e. [equals] returns true) but they are both [snapshot] then they may refer to different
* builds of the node. [NodeVersionInfo.revision] would be required to differentiate the two.
*/
@CordaSerializable
data class Version(val major: Int, val minor: Int, val patch: Int?, val snapshot: Boolean) {
companion object {
private val pattern = Pattern.compile("""(\d+)\.(\d+)(.(\d+))?(-SNAPSHOT)?""")
fun parse(string: String): Version {
val matcher = pattern.matcher(string)
require(matcher.matches())
val patch = matcher.group(4)?.toInt()
return Version(matcher.group(1).toInt(), matcher.group(2).toInt(), patch, matcher.group(5) != null)
}
}
override fun toString(): String {
val sb = StringBuilder()
sb.append(major, ".", minor)
if(patch != null) sb.append(".", patch)
if(snapshot) sb.append("-SNAPSHOT")
return sb.toString()
}
}
data class NodeVersionInfo(val version: Version, val revision: String, val vendor: String)

View File

@ -0,0 +1,17 @@
package net.corda.core.node
/**
* Encapsulates various pieces of version information of the node.
*/
data class VersionInfo(
/**
* Platform version of the node which is an integer value which increments on any release where any of the public
* API of the entire Corda platform changes. This includes messaging, serialisation, node APIs, etc.
*/
val platformVersion: Int,
/** Release version string of the node. */
val releaseVersion: String,
/** The exact version control commit ID of the node build. */
val revision: String,
/** The node vendor */
val vendor: String)

View File

@ -1,41 +0,0 @@
package net.corda.core.node
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
class VersionTest {
@Test
fun `parse valid non-SNAPSHOT string`() {
assertThat(Version.parse("1.2")).isEqualTo(Version(1, 2, null, false))
}
@Test
fun `parse valid SNAPSHOT string`() {
assertThat(Version.parse("2.23-SNAPSHOT")).isEqualTo(Version(2, 23, null, true))
}
@Test
fun `parse string with just major number`() {
assertThatThrownBy {
Version.parse("2")
}.isInstanceOf(IllegalArgumentException::class.java)
}
@Test
fun `parse string with unknown qualifier`() {
assertThatThrownBy {
Version.parse("2.3-TEST")
}.isInstanceOf(IllegalArgumentException::class.java)
}
@Test
fun `parses patch version`() {
assertThat(Version.parse("0.1.2")).isEqualTo(Version(0, 1, 2, false))
}
@Test
fun `parses snapshot patch version`() {
assertThat(Version.parse("0.1.2-SNAPSHOT")).isEqualTo(Version(0, 1, 2, true))
}
}

View File

@ -57,11 +57,11 @@ the ``cancel`` method on the future will unsubscribe it from any future value an
Versioning Versioning
---------- ----------
The client RPC protocol is versioned with a simple incrementing integer. When a proxy is created the server is The client RPC protocol is versioned using the node's Platform Version (see :doc:`versioning`). When a proxy is created
queried for its protocol version, and you can specify your minimum requirement. Methods added in later versions the server is queried for its version, and you can specify your minimum requirement. Methods added in later versions
are tagged with the ``@RPCSinceVersion`` annotation. If you try to use a method that the server isn't advertising are tagged with the ``@RPCSinceVersion`` annotation. If you try to use a method that the server isn't advertising support
support of, an ``UnsupportedOperationException`` is thrown. If you want to know the version of the server, just of, an ``UnsupportedOperationException`` is thrown. If you want to know the version of the server, just use the
use the ``protocolVersion`` property (i.e. ``getProtocolVersion`` in Java). ``protocolVersion`` property (i.e. ``getProtocolVersion`` in Java).
Thread safety Thread safety
------------- -------------

View File

@ -116,6 +116,10 @@ path to the node's base directory.
:legalName: Legal name of the node. This is required as part of the TLS host verification process. The node will :legalName: Legal name of the node. This is required as part of the TLS host verification process. The node will
reject the connection to the network map service if it provides a TLS common name which doesn't match with this value. reject the connection to the network map service if it provides a TLS common name which doesn't match with this value.
:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement
on registrations - any node on a Platform Version lower than this value will have their registration rejected.
Defaults to 1 if absent.
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private :useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
then unencrypted HTTP traffic to the node's **webAddress** port is not supported. then unencrypted HTTP traffic to the node's **webAddress** port is not supported.

View File

@ -128,8 +128,8 @@ the following segments to the relevant part of your build.gradle.
.. code-block:: groovy .. code-block:: groovy
buildscript { buildscript {
ext.corda_version = '<enter the corda version you build against here>' ext.corda_release_version = '<enter the corda version you build against here>'
ext.corda_gradle_plugins_version = '<enter the gradle plugins version here>' // This is usually the same as corda_version. ext.corda_gradle_plugins_version = '<enter the gradle plugins version here>' // This is usually the same as corda_release_version.
... your buildscript ... ... your buildscript ...
repositories { repositories {
@ -155,10 +155,10 @@ the following segments to the relevant part of your build.gradle.
} }
dependencies { dependencies {
compile "net.corda.core:$corda_version" compile "net.corda.core:$corda_release_version"
compile "net.corda.finance:$corda_version" compile "net.corda.finance:$corda_release_version"
compile "net.corda.node:$corda_version" compile "net.corda.node:$corda_release_version"
compile "net.corda.corda:$corda_version" compile "net.corda.corda:$corda_release_version"
... other dependencies here ... ... other dependencies here ...
} }

View File

@ -72,6 +72,7 @@ Documentation Contents:
:maxdepth: 2 :maxdepth: 2
:caption: The Corda node :caption: The Corda node
versioning
shell shell
serialization serialization
clientrpc clientrpc

View File

@ -22,12 +22,21 @@ X.500 distinguished names (see RFC 1779 for details on the construction of disti
* If you are using mock parties for testing, try to standardise on the ``DUMMY_NOTARY``, ``DUMMY_BANK_A``, etc. provided * If you are using mock parties for testing, try to standardise on the ``DUMMY_NOTARY``, ``DUMMY_BANK_A``, etc. provided
in order to ensure consistency. in order to ensure consistency.
We have updated DemoBench so that it is installed as "Corda DemoBench" for both Windows and MacOSX. The original version was installed as just "DemoBench", and so will not be overwritten automatically by the new version. We have updated DemoBench so that it is installed as "Corda DemoBench" for both Windows and MacOSX. The original version
was installed as just "DemoBench", and so will not be overwritten automatically by the new version.
We've introduced the concept of platform version which is a single integer value which increments by 1 if a release changes
any of the public APIs of the entire Corda platform. This includes the node's public APIs, the messaging protocol,
serialisation, etc. The node exposes the platform version it's on and we envision CorDapps will use this to be able to
run on older versions of the platform to the one they were compiled against. Platform version borrows heavily from Android's
API Level.
Milestone 10 Milestone 10
------------ ------------
Special thank you to `Qian Hong <https://github.com/fracting>`_, `Marek Skocovsky <https://github.com/marekdapps>`_, `Karel Hajek <https://github.com/polybioz>`_, and `Jonny Chiu <https://github.com/johnnyychiu>`_ for their contributions to Corda in M10. Special thank you to `Qian Hong <https://github.com/fracting>`_, `Marek Skocovsky <https://github.com/marekdapps>`_,
`Karel Hajek <https://github.com/polybioz>`_, and `Jonny Chiu <https://github.com/johnnyychiu>`_ for their contributions
to Corda in M10.
A new interactive **Corda Shell** has been added to the node. The shell lets developers and node administrators A new interactive **Corda Shell** has been added to the node. The shell lets developers and node administrators
easily command the node by running flows, RPCs and SQL queries. It also provides a variety of commands to monitor easily command the node by running flows, RPCs and SQL queries. It also provides a variety of commands to monitor
@ -35,25 +44,34 @@ the node. The Corda Shell is based on the popular `CRaSH project <http://www.cra
be easily added to the node by simply dropping Groovy or Java files into the node's ``shell-commands`` directory. be easily added to the node by simply dropping Groovy or Java files into the node's ``shell-commands`` directory.
We have many enhancements planned over time including SSH access, more commands and better tab completion. We have many enhancements planned over time including SSH access, more commands and better tab completion.
The new "DemoBench" makes it easy to configure and launch local Corda nodes. It is a standalone desktop app that can be bundled with its own JRE and packaged as either EXE (Windows), DMG (MacOS) or RPM (Linux-based). It has the following features: The new "DemoBench" makes it easy to configure and launch local Corda nodes. It is a standalone desktop app that can be
#. New nodes can be added at the click of a button. Clicking "Add node" creates a new tab that lets you edit the most important configuration properties of the node before launch, such as its legal name and which CorDapps will be loaded. bundled with its own JRE and packaged as either EXE (Windows), DMG (MacOS) or RPM (Linux-based). It has the following
features:
#. New nodes can be added at the click of a button. Clicking "Add node" creates a new tab that lets you edit the most
important configuration properties of the node before launch, such as its legal name and which CorDapps will be loaded.
#. Each tab contains a terminal emulator, attached to the pseudoterminal of the node. This lets you see console output. #. Each tab contains a terminal emulator, attached to the pseudoterminal of the node. This lets you see console output.
#. You can launch an Corda Explorer instance for each node at the click of a button. Credentials are handed to the Corda Explorer so it starts out logged in already. #. You can launch an Corda Explorer instance for each node at the click of a button. Credentials are handed to the Corda
Explorer so it starts out logged in already.
#. Some basic statistics are shown about each node, informed via the RPC connection. #. Some basic statistics are shown about each node, informed via the RPC connection.
#. Another button launches a database viewer in the system browser. #. Another button launches a database viewer in the system browser.
#. The configurations of all running nodes can be saved into a single ``.profile`` file that can be reloaded later. #. The configurations of all running nodes can be saved into a single ``.profile`` file that can be reloaded later.
Soft Locking is a new feature implemented in the vault to prevent a node constructing transactions that attempt to use the same input(s) simultaneously. Soft Locking is a new feature implemented in the vault to prevent a node constructing transactions that attempt to use the
Such transactions would result in naturally wasted effort when the notary rejects them as double spend attempts. same input(s) simultaneously. Such transactions would result in naturally wasted effort when the notary rejects them as
Soft locks are automatically applied to coin selection (eg. cash spending) to ensure that no two transactions attempt to spend the same fungible states. double spend attempts. Soft locks are automatically applied to coin selection (eg. cash spending) to ensure that no two
transactions attempt to spend the same fungible states.
The basic Amount API has been upgraded to have support for advanced financial use cases and to better integrate with currency reference data. The basic Amount API has been upgraded to have support for advanced financial use cases and to better integrate with
currency reference data.
We have added optional out-of-process transaction verification. Any number of external verifier processes may be attached to the node which can handle loadbalanced verification requests. We have added optional out-of-process transaction verification. Any number of external verifier processes may be attached
to the node which can handle loadbalanced verification requests.
We have also delivered the long waited Kotlin 1.1 upgrade in M10! The new features in Kotlin allow us to write even more clean and easy to manage code, which greatly increases our productivity. We have also delivered the long waited Kotlin 1.1 upgrade in M10! The new features in Kotlin allow us to write even more
clean and easy to manage code, which greatly increases our productivity.
This release contains a large number of improvements, new features, library upgrades and bug fixes. For a full list of changes please see :doc:`changelog`. This release contains a large number of improvements, new features, library upgrades and bug fixes. For a full list of
changes please see :doc:`changelog`.
Milestone 9 Milestone 9
----------- -----------

View File

@ -53,8 +53,8 @@ for you from our `public Maven repository <https://bintray.com/r3/corda>`_.
**Using a Corda SNAPSHOT build.** Alternatively, if you wish to work from the master branch of the Corda repo which contains **Using a Corda SNAPSHOT build.** Alternatively, if you wish to work from the master branch of the Corda repo which contains
the most up-to-date Corda feature set then you will need to clone the ``corda`` repository and publish the latest master the most up-to-date Corda feature set then you will need to clone the ``corda`` repository and publish the latest master
build (or previously tagged releases) to your local Maven repository. You will then need to ensure that Gradle build (or previously tagged releases) to your local Maven repository. You will then need to ensure that Gradle
grabs the correct dependencies for you from Maven local by changing the ``corda_version`` in ``build.gradle``. This will be grabs the correct dependencies for you from Maven local by changing the ``corda_release_version`` in ``build.gradle``.
covered below in `Using a SNAPSHOT release`_. This will be covered below in `Using a SNAPSHOT release`_.
Firstly, follow the :doc:`getting set up <getting-set-up>` page to download the JDK, IntelliJ and git if you didn't Firstly, follow the :doc:`getting set up <getting-set-up>` page to download the JDK, IntelliJ and git if you didn't
already have it. already have it.
@ -145,10 +145,10 @@ if you are trying out new features, in this case you can change ``version`` for
.. note:: **A quick point on corda version numbers used by Gradle.** .. note:: **A quick point on corda version numbers used by Gradle.**
In the ``build.gradle`` file for your CorDapp, you can specify the ``corda_version`` to use. It is important that when In the ``build.gradle`` file for your CorDapp, you can specify the ``corda_release_version`` to use. It is important
developing your CorDapp that you use the correct version number. For example, when wanting to work from a SNAPSHOT that when developing your CorDapp that you use the correct version number. For example, when wanting to work from a SNAPSHOT
release, the release numbers are suffixed with 'SNAPSHOT', e.g. if the latest milestone release is M6 then the release, the release numbers are suffixed with 'SNAPSHOT', e.g. if the latest milestone release is M6 then the
SNAPSHOT release will be 0.7-SNAPSHOT, and so on. As such, you will set your ``corda_version`` to ``'0.7-SNAPSHOT'`` SNAPSHOT release will be 0.7-SNAPSHOT, and so on. As such, you will set your ``corda_release_version`` to ``'0.7-SNAPSHOT'``
in the ``build.gradle`` file in your CorDapp. Gradle will automatically grab the SNAPSHOT dependencies from your local in the ``build.gradle`` file in your CorDapp. Gradle will automatically grab the SNAPSHOT dependencies from your local
Maven repository. Alternatively, if working from a milestone release, you will use the version number only, for example Maven repository. Alternatively, if working from a milestone release, you will use the version number only, for example
``0.6`` or ``0.7``. ``0.6`` or ``0.7``.
@ -224,7 +224,7 @@ see all available Gradle tasks.
* For the Corda repo there will be many project listed, the root project ``corda`` and associated sub-projects: ``core``, * For the Corda repo there will be many project listed, the root project ``corda`` and associated sub-projects: ``core``,
``finance``, ``node``, etc. ``finance``, ``node``, etc.
.. note:: It's worth noting that when you change branch in the example CorDapp, the ``corda_version`` will change to .. note:: It's worth noting that when you change branch in the example CorDapp, the ``corda_release_version`` will change to
reflect the version of the branch you are working from. reflect the version of the branch you are working from.
To execute a task, double click it. The output will be shown in a console window. To execute a task, double click it. The output will be shown in a console window.
@ -712,15 +712,15 @@ are available for use in the rest of the build script. It also specifies version
things. things.
If you are working from a Corda SNAPSHOT release which you have publish to Maven local then ensure that If you are working from a Corda SNAPSHOT release which you have publish to Maven local then ensure that
``corda_version`` is the same as the version of the Corda core modules you published to Maven local. If not then change the ``corda_release_version`` is the same as the version of the Corda core modules you published to Maven local. If not then
``kotlin_version`` property. Also, if you are working from a previous cordapp-tutorial milestone release, then be sure to ``git checkout`` change the ``kotlin_version`` property. Also, if you are working from a previous cordapp-tutorial milestone release, then
the correct version of the example CorDapp from the ``cordapp-tutorial`` repo. be sure to ``git checkout`` the correct version of the example CorDapp from the ``cordapp-tutorial`` repo.
.. sourcecode:: groovy .. sourcecode:: groovy
buildscript { buildscript {
ext.kotlin_version = '1.0.4' ext.kotlin_version = '1.0.4'
ext.corda_version = '0.5-SNAPSHOT' // Ensure this version is the same as the corda core modules you are using. ext.corda_release_version = '0.5-SNAPSHOT' // Ensure this version is the same as the corda core modules you are using.
ext.quasar_version = '0.7.6' ext.quasar_version = '0.7.6'
ext.jersey_version = '2.23.1' ext.jersey_version = '2.23.1'
@ -747,12 +747,12 @@ code snippet.package. Use the standard format:
testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'junit', name: 'junit', version: '4.11'
// Corda integration dependencies // Corda integration dependencies
compile "net.corda:client:$corda_version" compile "net.corda:client:$corda_release_version"
compile "net.corda:core:$corda_version" compile "net.corda:core:$corda_release_version"
compile "net.corda:contracts:$corda_version" compile "net.corda:contracts:$corda_release_version"
compile "net.corda:node:$corda_version" compile "net.corda:node:$corda_release_version"
compile "net.corda:corda:$corda_version" compile "net.corda:corda:$corda_release_version"
compile "net.corda:test-utils:$corda_version" compile "net.corda:test-utils:$corda_release_version"
... ...

View File

@ -0,0 +1,29 @@
Versioning
==========
As the Corda platform evolves and new features are added it becomes important to have a versioning system which allows
its users to easily compare versions and know what feature are available to them. Each Corda release uses the standard
semantic versioning scheme of ``major.minor.patch``. This is useful when making releases in the public domain but is not
friendly for a developer working on the platform. It first has to be parsed and then they have three separate segments on
which to determine API differences. The release version is still useful and every MQ message the node sends attaches it
to the ``release-version`` header property for debugging purposes.
Platform Version
----------------
It is much easier to use a single incrementing integer value to represent the API version of the Corda platform, which
is called the Platform Version. It is similar to Android's `API Level <https://developer.android.com/guide/topics/manifest/uses-sdk-element.html>`_.
It starts at 1 and will increment by exactly 1 for each release which changes any of the publicly exposed APIs in the
entire platform. This includes public APIs on the node itself, the RPC system, messaging, serialisation, etc. API backwards
compatibility will always be maintained, with the use of deprecation to migrate away from old APIs. In rare situations
APIs may have to be removed, for example due to security issues. There is no relationship between the Platform Version
and the release version - a change in the major, minor or patch values may or may not increase the Platform Version.
The Platform Version is part of the node's ``NodeInfo`` object, which is available from the ``ServiceHub``. This enables
a CorDapp to find out which version it's running on and determine whether a desired feature is available. When a node
registers with the Network Map Service it will use the node's Platform Version to enforce a minimum version requirement
for the network.
.. note:: A future release may introduce the concept of a target platform version, which would be similar to Android's
``targetSdkVersion``, and would provide a means of maintaining behavioural compatibility for the cases where the
platform's behaviour has changed.

View File

@ -274,10 +274,10 @@ class Node {
*/ */
private File verifyAndGetCordaJar() { private File verifyAndGetCordaJar() {
def maybeCordaJAR = project.configurations.runtime.filter { def maybeCordaJAR = project.configurations.runtime.filter {
it.toString().contains("corda-${project.corda_version}.jar") it.toString().contains("corda-${project.corda_release_version}.jar")
} }
if (maybeCordaJAR.size() == 0) { if (maybeCordaJAR.size() == 0) {
throw new RuntimeException("No Corda Capsule JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-${project.corda_version}.jar\"") throw new RuntimeException("No Corda Capsule JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-${project.corda_release_version}.jar\"")
} else { } else {
def cordaJar = maybeCordaJAR.getSingleFile() def cordaJar = maybeCordaJAR.getSingleFile()
assert(cordaJar.isFile()) assert(cordaJar.isFile())
@ -292,10 +292,10 @@ class Node {
*/ */
private File verifyAndGetWebserverJar() { private File verifyAndGetWebserverJar() {
def maybeJar = project.configurations.runtime.filter { def maybeJar = project.configurations.runtime.filter {
it.toString().contains("corda-webserver-${project.corda_version}.jar") it.toString().contains("corda-webserver-${project.corda_release_version}.jar")
} }
if (maybeJar.size() == 0) { if (maybeJar.size() == 0) {
throw new RuntimeException("No Corda Webserver JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-${project.corda_version}.jar\"") throw new RuntimeException("No Corda Webserver JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-${project.corda_release_version}.jar\"")
} else { } else {
def jar = maybeJar.getSingleFile() def jar = maybeJar.getSingleFile()
assert(jar.isFile()) assert(jar.isFile())

View File

@ -38,12 +38,12 @@ dependencies {
task buildCordaJAR(type: FatCapsule) { task buildCordaJAR(type: FatCapsule) {
applicationClass 'net.corda.node.Corda' applicationClass 'net.corda.node.Corda'
archiveName "corda-${corda_version}.jar" archiveName "corda-${corda_release_version}.jar"
applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/CordaCaplet.class', '../build/classes/main/CordaCaplet$1.class', 'config/dev/log4j2.xml') applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/CordaCaplet.class', '../build/classes/main/CordaCaplet$1.class', 'config/dev/log4j2.xml')
from 'NOTICE' // Copy CDDL notice from 'NOTICE' // Copy CDDL notice
capsuleManifest { capsuleManifest {
applicationVersion = corda_version applicationVersion = corda_release_version
appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"]
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
systemProperties['visualvm.display.name'] = 'Corda' systemProperties['visualvm.display.name'] = 'Corda'
@ -63,12 +63,6 @@ task buildCordaJAR(type: FatCapsule) {
// This lets you run the file like so: ./corda.jar // This lets you run the file like so: ./corda.jar
// Other than being slightly less typing, this has one big advantage: Ctrl-C works properly in the terminal. // Other than being slightly less typing, this has one big advantage: Ctrl-C works properly in the terminal.
reallyExecutable { trampolining() } reallyExecutable { trampolining() }
manifest {
attributes('Corda-Version': corda_version)
attributes('Corda-Revision': corda_revision)
attributes('Corda-Vendor': 'Corda Open Source')
}
} }
artifacts { artifacts {

View File

@ -15,7 +15,7 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.network.NetworkMapService.RegistrationRequest import net.corda.node.services.network.NetworkMapService.RegistrationRequest
import net.corda.node.services.network.NodeRegistration import net.corda.node.services.network.NodeRegistration
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.testing.MOCK_VERSION import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import net.corda.testing.node.SimpleNode import net.corda.testing.node.SimpleNode
@ -63,7 +63,7 @@ class P2PSecurityTest : NodeBasedTest() {
} }
private fun SimpleNode.registerWithNetworkMap(registrationName: String): ListenableFuture<NetworkMapService.RegistrationResponse> { private fun SimpleNode.registerWithNetworkMap(registrationName: String): ListenableFuture<NetworkMapService.RegistrationResponse> {
val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION) val nodeInfo = NodeInfo(net.myAddress, Party(registrationName, identity.public), MOCK_VERSION_INFO.platformVersion)
val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX)
val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress) val request = RegistrationRequest(registration.toWire(identity.private), net.myAddress)
return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress) return net.sendRequest<NetworkMapService.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.net.myAddress)

View File

@ -6,8 +6,7 @@ import com.jcabi.manifests.Manifests
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import joptsimple.OptionException import joptsimple.OptionException
import net.corda.core.* import net.corda.core.*
import net.corda.core.node.NodeVersionInfo import net.corda.core.node.VersionInfo
import net.corda.core.node.Version
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.core.utilities.LogHelper.withLevel import net.corda.core.utilities.LogHelper.withLevel
import net.corda.node.internal.Node import net.corda.node.internal.Node
@ -69,15 +68,17 @@ fun main(args: Array<String>) {
// Manifest properties are only available if running from the corda jar // Manifest properties are only available if running from the corda jar
fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null
val nodeVersionInfo = NodeVersionInfo( val versionInfo = VersionInfo(
manifestValue("Corda-Version")?.let { Version.parse(it) } ?: Version(0, 0, 0, false), manifestValue("Corda-Platform-Version")?.toInt() ?: 1,
manifestValue("Corda-Release-Version") ?: "Unknown",
manifestValue("Corda-Revision") ?: "Unknown", manifestValue("Corda-Revision") ?: "Unknown",
manifestValue("Corda-Vendor") ?: "Unknown" manifestValue("Corda-Vendor") ?: "Unknown"
) )
if (cmdlineOptions.isVersion) { if (cmdlineOptions.isVersion) {
println("${nodeVersionInfo.vendor} ${nodeVersionInfo.version}") println("${versionInfo.vendor} ${versionInfo.releaseVersion}")
println("Revision ${nodeVersionInfo.revision}") println("Revision ${versionInfo.revision}")
println("Platform Version ${versionInfo.platformVersion}")
exitProcess(0) exitProcess(0)
} }
@ -87,7 +88,7 @@ fun main(args: Array<String>) {
exitProcess(0) exitProcess(0)
} }
drawBanner(nodeVersionInfo) drawBanner(versionInfo)
val log = LoggerFactory.getLogger("Main") val log = LoggerFactory.getLogger("Main")
printBasicNodeInfo("Logs can be found in", System.getProperty("log-path")) printBasicNodeInfo("Logs can be found in", System.getProperty("log-path"))
@ -110,9 +111,10 @@ fun main(args: Array<String>) {
exitProcess(0) exitProcess(0)
} }
log.info("Version: ${nodeVersionInfo.version}") log.info("Vendor: ${versionInfo.vendor}")
log.info("Vendor: ${nodeVersionInfo.vendor}") log.info("Release: ${versionInfo.releaseVersion}")
log.info("Revision: ${nodeVersionInfo.revision}") log.info("Platform Version: ${versionInfo.platformVersion}")
log.info("Revision: ${versionInfo.revision}")
val info = ManagementFactory.getRuntimeMXBean() val info = ManagementFactory.getRuntimeMXBean()
log.info("PID: ${info.name.split("@").firstOrNull()}") // TODO Java 9 has better support for this log.info("PID: ${info.name.split("@").firstOrNull()}") // TODO Java 9 has better support for this
log.info("Main class: ${FullNodeConfiguration::class.java.protectionDomain.codeSource.location.toURI().path}") log.info("Main class: ${FullNodeConfiguration::class.java.protectionDomain.codeSource.location.toURI().path}")
@ -132,7 +134,7 @@ fun main(args: Array<String>) {
try { try {
cmdlineOptions.baseDirectory.createDirectories() cmdlineOptions.baseDirectory.createDirectories()
val node = conf.createNode(nodeVersionInfo) val node = conf.createNode(versionInfo)
node.start() node.start()
printPluginsAndServices(node) printPluginsAndServices(node)
@ -236,7 +238,7 @@ private fun messageOfTheDay(): Pair<String, String> {
return Pair(a, b) return Pair(a, b)
} }
private fun drawBanner(nodeVersionInfo: NodeVersionInfo) { private fun drawBanner(versionInfo: VersionInfo) {
// This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box. // This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box.
AnsiConsole.systemInstall() AnsiConsole.systemInstall()
@ -249,7 +251,7 @@ private fun drawBanner(nodeVersionInfo: NodeVersionInfo) {
/ / __ / ___/ __ / __ `/ """).fgBrightBlue().a(msg1).newline().fgBrightRed().a( / / __ / ___/ __ / __ `/ """).fgBrightBlue().a(msg1).newline().fgBrightRed().a(
"/ /___ /_/ / / / /_/ / /_/ / ").fgBrightBlue().a(msg2).newline().fgBrightRed().a( "/ /___ /_/ / / / /_/ / /_/ / ").fgBrightBlue().a(msg2).newline().fgBrightRed().a(
"""\____/ /_/ \__,_/\__,_/""").reset().newline().newline().fgBrightDefault().bold(). """\____/ /_/ \__,_/\__,_/""").reset().newline().newline().fgBrightDefault().bold().
a("--- ${nodeVersionInfo.vendor} ${nodeVersionInfo.version} (${nodeVersionInfo.revision.take(7)}) -----------------------------------------------"). a("--- ${versionInfo.vendor} ${versionInfo.releaseVersion} (${versionInfo.revision.take(7)}) -----------------------------------------------").
newline(). newline().
newline(). newline().
a("${Emoji.books}New! ").reset().a("Training now available worldwide, see https://corda.net/corda-training/"). a("${Emoji.books}New! ").reset().a("Training now available worldwide, see https://corda.net/corda-training/").

View File

@ -101,7 +101,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected abstract val log: Logger protected abstract val log: Logger
protected abstract val networkMapAddress: SingleMessageRecipient? protected abstract val networkMapAddress: SingleMessageRecipient?
protected abstract val version: Version protected abstract val platformVersion: Int
// We will run as much stuff in this single thread as possible to keep the risk of thread safety bugs low during the // We will run as much stuff in this single thread as possible to keep the risk of thread safety bugs low during the
// low-performance prototyping period. // low-performance prototyping period.
@ -296,7 +296,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private fun makeInfo(): NodeInfo { private fun makeInfo(): NodeInfo {
val advertisedServiceEntries = makeServiceEntries() val advertisedServiceEntries = makeServiceEntries()
val legalIdentity = obtainLegalIdentity() val legalIdentity = obtainLegalIdentity()
return NodeInfo(net.myAddress, legalIdentity, version, advertisedServiceEntries, findMyLocation()) return NodeInfo(net.myAddress, legalIdentity, platformVersion, advertisedServiceEntries, findMyLocation())
} }
/** /**
@ -445,7 +445,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected open fun makeKeyManagementService(): KeyManagementService = PersistentKeyManagementService(partyKeys) protected open fun makeKeyManagementService(): KeyManagementService = PersistentKeyManagementService(partyKeys)
open protected fun makeNetworkMapService() { open protected fun makeNetworkMapService() {
inNodeNetworkMapService = PersistentNetworkMapService(services) inNodeNetworkMapService = PersistentNetworkMapService(services, configuration.minimumPlatformVersion)
} }
open protected fun makeNotaryService(type: ServiceType, tokenizableServices: MutableList<Any>): NotaryService { open protected fun makeNotaryService(type: ServiceType, tokenizableServices: MutableList<Any>): NotaryService {

View File

@ -5,14 +5,15 @@ import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.* import net.corda.core.div
import net.corda.core.flatMap
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.Version import net.corda.core.node.VersionInfo
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.node.services.UniquenessProvider import net.corda.core.node.services.UniquenessProvider
import net.corda.core.success
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.printBasicNodeInfo import net.corda.node.printBasicNodeInfo
import net.corda.node.serialization.NodeClock import net.corda.node.serialization.NodeClock
@ -48,14 +49,14 @@ import kotlin.concurrent.thread
*/ */
class Node(override val configuration: FullNodeConfiguration, class Node(override val configuration: FullNodeConfiguration,
advertisedServices: Set<ServiceInfo>, advertisedServices: Set<ServiceInfo>,
val nodeVersionInfo: NodeVersionInfo, val versionInfo: VersionInfo,
clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) { clock: Clock = NodeClock()) : AbstractNode(configuration, advertisedServices, clock) {
companion object { companion object {
private val logger = loggerFor<Node>() private val logger = loggerFor<Node>()
} }
override val log: Logger get() = logger override val log: Logger get() = logger
override val version: Version get() = nodeVersionInfo.version override val platformVersion: Int get() = versionInfo.platformVersion
override val networkMapAddress: NetworkMapAddress? get() = configuration.networkMapService?.address?.let(::NetworkMapAddress) override val networkMapAddress: NetworkMapAddress? get() = configuration.networkMapService?.address?.let(::NetworkMapAddress)
override fun makeTransactionVerifierService() = (net as NodeMessagingClient).verifierService override fun makeTransactionVerifierService() = (net as NodeMessagingClient).verifierService
@ -108,32 +109,13 @@ class Node(override val configuration: FullNodeConfiguration,
private lateinit var userService: RPCUserService private lateinit var userService: RPCUserService
init {
checkVersionUnchanged()
}
/**
* Abort starting the node if an existing deployment with a different version is detected in the base directory.
*/
private fun checkVersionUnchanged() {
val versionFile = configuration.baseDirectory / "version"
if (versionFile.exists()) {
val previousVersion = Version.parse(versionFile.readAllLines()[0])
check(nodeVersionInfo.version.major == previousVersion.major) {
"Major version change detected - current: ${nodeVersionInfo.version}, previous: $previousVersion. " +
"Node upgrades across major versions are not yet supported."
}
}
versionFile.writeLines(listOf(nodeVersionInfo.version.toString()))
}
override fun makeMessagingService(): MessagingServiceInternal { override fun makeMessagingService(): MessagingServiceInternal {
userService = RPCUserServiceImpl(configuration.rpcUsers) userService = RPCUserServiceImpl(configuration.rpcUsers)
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null
return NodeMessagingClient( return NodeMessagingClient(
configuration, configuration,
nodeVersionInfo, versionInfo,
serverAddress, serverAddress,
myIdentityOrNullIfNetworkMapService, myIdentityOrNullIfNetworkMapService,
serverThread, serverThread,

View File

@ -2,7 +2,7 @@ package net.corda.node.services.config
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.core.div import net.corda.core.div
import net.corda.core.node.NodeVersionInfo import net.corda.core.node.VersionInfo
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.node.internal.NetworkMapInfo import net.corda.node.internal.NetworkMapInfo
import net.corda.node.internal.Node import net.corda.node.internal.Node
@ -22,6 +22,7 @@ interface NodeConfiguration : SSLConfiguration {
override val certificatesDirectory: Path get() = baseDirectory / "certificates" override val certificatesDirectory: Path get() = baseDirectory / "certificates"
val myLegalName: String val myLegalName: String
val networkMapService: NetworkMapInfo? val networkMapService: NetworkMapInfo?
val minimumPlatformVersion: Int
val nearestCity: String val nearestCity: String
val emailAddress: String val emailAddress: String
val exportJMXto: String val exportJMXto: String
@ -47,6 +48,7 @@ data class FullNodeConfiguration(
override val dataSourceProperties: Properties, override val dataSourceProperties: Properties,
override val certificateSigningService: URL, override val certificateSigningService: URL,
override val networkMapService: NetworkMapInfo?, override val networkMapService: NetworkMapInfo?,
override val minimumPlatformVersion: Int = 1,
override val rpcUsers: List<User>, override val rpcUsers: List<User>,
override val verifierType: VerifierType, override val verifierType: VerifierType,
val useHTTPS: Boolean, val useHTTPS: Boolean,
@ -78,14 +80,14 @@ data class FullNodeConfiguration(
} }
} }
fun createNode(nodeVersionInfo: NodeVersionInfo): Node { fun createNode(versionInfo: VersionInfo): Node {
val advertisedServices = extraAdvertisedServiceIds val advertisedServices = extraAdvertisedServiceIds
.filter(String::isNotBlank) .filter(String::isNotBlank)
.map { ServiceInfo.parse(it) } .map { ServiceInfo.parse(it) }
.toMutableSet() .toMutableSet()
if (networkMapService == null) advertisedServices += ServiceInfo(NetworkMapService.type) if (networkMapService == null) advertisedServices += ServiceInfo(NetworkMapService.type)
return Node(this, advertisedServices, nodeVersionInfo, if (useTestClock) TestClock() else NodeClock()) return Node(this, advertisedServices, versionInfo, if (useTestClock) TestClock() else NodeClock())
} }
} }

View File

@ -4,7 +4,7 @@ import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeVersionInfo import net.corda.core.node.VersionInfo
import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.PartyInfo
import net.corda.core.node.services.TransactionVerifierService import net.corda.core.node.services.TransactionVerifierService
import net.corda.core.random63BitValue import net.corda.core.random63BitValue
@ -68,7 +68,7 @@ import java.security.PublicKey
*/ */
@ThreadSafe @ThreadSafe
class NodeMessagingClient(override val config: NodeConfiguration, class NodeMessagingClient(override val config: NodeConfiguration,
nodeVersionInfo: NodeVersionInfo, val versionInfo: VersionInfo,
val serverHostPort: HostAndPort, val serverHostPort: HostAndPort,
val myIdentity: PublicKey?, val myIdentity: PublicKey?,
val nodeExecutor: AffinityExecutor, val nodeExecutor: AffinityExecutor,
@ -85,9 +85,10 @@ class NodeMessagingClient(override val config: NodeConfiguration,
// confusion. // confusion.
private val topicProperty = SimpleString("platform-topic") private val topicProperty = SimpleString("platform-topic")
private val sessionIdProperty = SimpleString("session-id") private val sessionIdProperty = SimpleString("session-id")
private val nodeVersionProperty = SimpleString("node-version") private val cordaVendorProperty = SimpleString("corda-vendor")
private val nodeVendorProperty = SimpleString("node-vendor") private val releaseVersionProperty = SimpleString("release-version")
private val amqDelay: Int = Integer.valueOf(System.getProperty("amq.delivery.delay.ms", "0")) private val platformVersionProperty = SimpleString("platform-version")
private val amqDelayMillis = System.getProperty("amq.delivery.delay.ms", "0").toInt()
private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}" private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}"
} }
@ -114,8 +115,8 @@ class NodeMessagingClient(override val config: NodeConfiguration,
data class Handler(val topicSession: TopicSession, data class Handler(val topicSession: TopicSession,
val callback: (ReceivedMessage, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration val callback: (ReceivedMessage, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration
private val nodeVendor = SimpleString(nodeVersionInfo.vendor) private val cordaVendor = SimpleString(versionInfo.vendor)
private val version = SimpleString(nodeVersionInfo.version.toString()) private val releaseVersion = SimpleString(versionInfo.releaseVersion)
/** An executor for sending messages */ /** An executor for sending messages */
private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1) private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1)
@ -409,8 +410,9 @@ class NodeMessagingClient(override val config: NodeConfiguration,
state.locked { state.locked {
val mqAddress = getMQAddress(target) val mqAddress = getMQAddress(target)
val artemisMessage = session!!.createMessage(true).apply { val artemisMessage = session!!.createMessage(true).apply {
putStringProperty(nodeVendorProperty, nodeVendor) putStringProperty(cordaVendorProperty, cordaVendor)
putStringProperty(nodeVersionProperty, version) putStringProperty(releaseVersionProperty, releaseVersion)
putIntProperty(platformVersionProperty, versionInfo.platformVersion)
putStringProperty(topicProperty, SimpleString(message.topicSession.topic)) putStringProperty(topicProperty, SimpleString(message.topicSession.topic))
putLongProperty(sessionIdProperty, message.topicSession.sessionID) putLongProperty(sessionIdProperty, message.topicSession.sessionID)
writeBodyBufferBytes(message.data) writeBodyBufferBytes(message.data)
@ -418,8 +420,8 @@ class NodeMessagingClient(override val config: NodeConfiguration,
putStringProperty(HDR_DUPLICATE_DETECTION_ID, SimpleString(message.uniqueMessageId.toString())) putStringProperty(HDR_DUPLICATE_DETECTION_ID, SimpleString(message.uniqueMessageId.toString()))
// For demo purposes - if set then add a delay to messages in order to demonstrate that the flows are doing as intended // For demo purposes - if set then add a delay to messages in order to demonstrate that the flows are doing as intended
if (amqDelay > 0 && message.topicSession.topic == StateMachineManager.sessionTopic.topic) { if (amqDelayMillis > 0 && message.topicSession.topic == StateMachineManager.sessionTopic.topic) {
putLongProperty(HDR_SCHEDULED_DELIVERY_TIME, System.currentTimeMillis() + amqDelay) putLongProperty(HDR_SCHEDULED_DELIVERY_TIME, System.currentTimeMillis() + amqDelayMillis)
} }
} }
log.trace { log.trace {

View File

@ -109,7 +109,8 @@ interface NetworkMapService {
} }
@ThreadSafe @ThreadSafe
class InMemoryNetworkMapService(services: ServiceHubInternal) : AbstractNetworkMapService(services) { class InMemoryNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
: AbstractNetworkMapService(services, minimumPlatformVersion) {
override val nodeRegistrations: MutableMap<Party, NodeRegistrationInfo> = ConcurrentHashMap() override val nodeRegistrations: MutableMap<Party, NodeRegistrationInfo> = ConcurrentHashMap()
override val subscribers = ThreadBox(mutableMapOf<SingleMessageRecipient, LastAcknowledgeInfo>()) override val subscribers = ThreadBox(mutableMapOf<SingleMessageRecipient, LastAcknowledgeInfo>())
@ -126,7 +127,8 @@ class InMemoryNetworkMapService(services: ServiceHubInternal) : AbstractNetworkM
* subscriber clean up and is simpler to persist than the previous implementation based on a set of missing messages acks. * subscriber clean up and is simpler to persist than the previous implementation based on a set of missing messages acks.
*/ */
@ThreadSafe @ThreadSafe
abstract class AbstractNetworkMapService(services: ServiceHubInternal) : NetworkMapService, AbstractNodeService(services) { abstract class AbstractNetworkMapService(services: ServiceHubInternal,
val minimumPlatformVersion: Int) : NetworkMapService, AbstractNodeService(services) {
companion object { companion object {
/** /**
* Maximum credible size for a registration request. Generally requests are around 500-600 bytes, so this gives a * Maximum credible size for a registration request. Generally requests are around 500-600 bytes, so this gives a
@ -152,6 +154,13 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network
private val handlers = ArrayList<MessageHandlerRegistration>() private val handlers = ArrayList<MessageHandlerRegistration>()
init {
require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" }
require(minimumPlatformVersion <= services.myInfo.platformVersion) {
"minimumPlatformVersion cannot be greater than the node's own version"
}
}
protected fun setup() { protected fun setup() {
// Register message handlers // Register message handlers
handlers += addMessageHandler(FETCH_TOPIC) { req: FetchMapRequest -> processFetchAllRequest(req) } handlers += addMessageHandler(FETCH_TOPIC) { req: FetchMapRequest -> processFetchAllRequest(req) }
@ -219,21 +228,27 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network
} }
private fun processRegistrationRequest(request: RegistrationRequest): RegistrationResponse { private fun processRegistrationRequest(request: RegistrationRequest): RegistrationResponse {
if (request.wireReg.raw.size > MAX_SIZE_REGISTRATION_REQUEST_BYTES) return RegistrationResponse("Request is too big") if (request.wireReg.raw.size > MAX_SIZE_REGISTRATION_REQUEST_BYTES) {
return RegistrationResponse("Request is too big")
}
val change = try { val change = try {
request.wireReg.verified() request.wireReg.verified()
} catch(e: SignatureException) { } catch (e: SignatureException) {
return RegistrationResponse("Invalid signature on request") return RegistrationResponse("Invalid signature on request")
} }
val node = change.node val node = change.node
if (node.platformVersion < minimumPlatformVersion) {
return RegistrationResponse("Minimum platform version requirement not met: $minimumPlatformVersion")
}
// Update the current value atomically, so that if multiple updates come // Update the current value atomically, so that if multiple updates come
// in on different threads, there is no risk of a race condition while checking // in on different threads, there is no risk of a race condition while checking
// sequence numbers. // sequence numbers.
val registrationInfo = try { val registrationInfo = try {
nodeRegistrations.compute(node.legalIdentity) { _: Party, existing: NodeRegistrationInfo? -> nodeRegistrations.compute(node.legalIdentity) { _, existing: NodeRegistrationInfo? ->
require(!((existing == null || existing.reg.type == REMOVE) && change.type == REMOVE)) { require(!((existing == null || existing.reg.type == REMOVE) && change.type == REMOVE)) {
"Attempting to de-register unknown node" "Attempting to de-register unknown node"
} }
@ -263,16 +278,16 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal) : Network
return RegistrationResponse(null) return RegistrationResponse(null)
} }
private fun notifySubscribers(wireReg: WireNodeRegistration, mapVersion: Int) { private fun notifySubscribers(wireReg: WireNodeRegistration, newMapVersion: Int) {
// TODO: Once we have a better established messaging system, we can probably send // TODO: Once we have a better established messaging system, we can probably send
// to a MessageRecipientGroup that nodes join/leave, rather than the network map // to a MessageRecipientGroup that nodes join/leave, rather than the network map
// service itself managing the group // service itself managing the group
val update = NetworkMapService.Update(wireReg, mapVersion, net.myAddress).serialize().bytes val update = NetworkMapService.Update(wireReg, newMapVersion, net.myAddress).serialize().bytes
val message = net.createMessage(PUSH_TOPIC, DEFAULT_SESSION_ID, update) val message = net.createMessage(PUSH_TOPIC, DEFAULT_SESSION_ID, update)
subscribers.locked { subscribers.locked {
// Remove any stale subscribers // Remove any stale subscribers
values.removeIf { lastAckInfo -> mapVersion - lastAckInfo.mapVersion > maxUnacknowledgedUpdates } values.removeIf { (mapVersion) -> newMapVersion - mapVersion > maxUnacknowledgedUpdates }
// TODO: introduce some concept of time in the condition to avoid unsubscribes when there's a message burst. // TODO: introduce some concept of time in the condition to avoid unsubscribes when there's a message burst.
keys.forEach { recipient -> net.send(message, recipient) } keys.forEach { recipient -> net.send(message, recipient) }
} }

View File

@ -17,7 +17,9 @@ import java.util.Collections.synchronizedMap
* This class needs database transactions to be in-flight during method calls and init, otherwise it will throw * This class needs database transactions to be in-flight during method calls and init, otherwise it will throw
* exceptions. * exceptions.
*/ */
class PersistentNetworkMapService(services: ServiceHubInternal) : AbstractNetworkMapService(services) { class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
: AbstractNetworkMapService(services, minimumPlatformVersion) {
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}network_map_nodes") { private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}network_map_nodes") {
val nodeParty = party("node_party_name", "node_party_key") val nodeParty = party("node_party_name", "node_party_key")
val registrationInfo = blob("node_registration_info") val registrationInfo = blob("node_registration_info")

View File

@ -25,7 +25,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.MOCK_NODE_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
@ -225,7 +225,7 @@ class ArtemisMessagingTests {
return database.transaction { return database.transaction {
NodeMessagingClient( NodeMessagingClient(
config, config,
MOCK_NODE_VERSION_INFO, MOCK_VERSION_INFO,
server, server,
identity.public, identity.public,
ServiceAffinityExecutor("ArtemisMessagingTests", 1), ServiceAffinityExecutor("ArtemisMessagingTests", 1),

View File

@ -5,6 +5,7 @@ import net.corda.core.node.services.ServiceInfo
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNetwork.MockNode
import java.math.BigInteger import java.math.BigInteger
@ -48,11 +49,11 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest<Persistent
* state within it is correctly restored. * state within it is correctly restored.
*/ */
private class SwizzleNetworkMapService(val services: ServiceHubInternal) : NetworkMapService { private class SwizzleNetworkMapService(val services: ServiceHubInternal) : NetworkMapService {
var delegate: PersistentNetworkMapService = PersistentNetworkMapService(services) var delegate: PersistentNetworkMapService = PersistentNetworkMapService(services, MOCK_VERSION_INFO.platformVersion)
fun swizzle() { fun swizzle() {
delegate.unregisterNetworkHandlers() delegate.unregisterNetworkHandlers()
delegate = PersistentNetworkMapService(services) delegate = PersistentNetworkMapService(services, MOCK_VERSION_INFO.platformVersion)
} }
} }
} }

View File

@ -6,11 +6,12 @@ package net.corda.testing
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.Version import net.corda.core.node.VersionInfo
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -83,8 +84,7 @@ val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, AL
val MOCK_IDENTITY_SERVICE: MockIdentityService get() = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY)) val MOCK_IDENTITY_SERVICE: MockIdentityService get() = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
val MOCK_VERSION = Version(0, 0, 0, false) val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor")
val MOCK_NODE_VERSION_INFO = NodeVersionInfo(MOCK_VERSION, "Mock revision", "Mock Vendor")
fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0) fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
@ -156,6 +156,7 @@ data class TestNodeConfiguration(
override val baseDirectory: Path, override val baseDirectory: Path,
override val myLegalName: String, override val myLegalName: String,
override val networkMapService: NetworkMapInfo?, override val networkMapService: NetworkMapInfo?,
override val minimumPlatformVersion: Int = 1,
override val keyStorePassword: String = "cordacadevpass", override val keyStorePassword: String = "cordacadevpass",
override val trustStorePassword: String = "trustpass", override val trustStorePassword: String = "trustpass",
override val rpcUsers: List<User> = emptyList(), override val rpcUsers: List<User> = emptyList(),
@ -166,7 +167,8 @@ data class TestNodeConfiguration(
override val devMode: Boolean = true, override val devMode: Boolean = true,
override val certificateSigningService: URL = URL("http://localhost"), override val certificateSigningService: URL = URL("http://localhost"),
override val certificateChainCheckPolicies: List<CertChainPolicyConfig> = emptyList(), override val certificateChainCheckPolicies: List<CertChainPolicyConfig> = emptyList(),
override val verifierType: VerifierType = VerifierType.InMemory) : NodeConfiguration override val verifierType: VerifierType = VerifierType.InMemory) : NodeConfiguration {
}
fun testConfiguration(baseDirectory: Path, legalName: String, basePort: Int): FullNodeConfiguration { fun testConfiguration(baseDirectory: Path, legalName: String, basePort: Int): FullNodeConfiguration {
return FullNodeConfiguration( return FullNodeConfiguration(

View File

@ -7,7 +7,7 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.node.services.network.InMemoryNetworkMapCache
import net.corda.testing.MOCK_VERSION import net.corda.testing.MOCK_VERSION_INFO
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
@ -20,8 +20,8 @@ class MockNetworkMapCache : InMemoryNetworkMapCache() {
data class MockAddress(val id: String) : SingleMessageRecipient data class MockAddress(val id: String) : SingleMessageRecipient
init { init {
val mockNodeA = NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C")), MOCK_VERSION) val mockNodeA = NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C")), MOCK_VERSION_INFO.platformVersion)
val mockNodeB = NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D")), MOCK_VERSION) val mockNodeB = NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D")), MOCK_VERSION_INFO.platformVersion)
registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA
registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB
runWithoutMapService() runWithoutMapService()

View File

@ -12,7 +12,6 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PhysicalLocation import net.corda.core.node.PhysicalLocation
import net.corda.core.node.ServiceEntry import net.corda.core.node.ServiceEntry
import net.corda.core.node.Version
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
@ -29,7 +28,7 @@ import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.testing.MOCK_VERSION import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger import org.slf4j.Logger
@ -136,7 +135,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
AbstractNode(config, advertisedServices, TestClock(), mockNet.busyLatch) { AbstractNode(config, advertisedServices, TestClock(), mockNet.busyLatch) {
var counter = entropyRoot var counter = entropyRoot
override val log: Logger = loggerFor<MockNode>() override val log: Logger = loggerFor<MockNode>()
override val version: Version get() = MOCK_VERSION override val platformVersion: Int get() = MOCK_VERSION_INFO.platformVersion
override val serverThread: AffinityExecutor = override val serverThread: AffinityExecutor =
if (mockNet.threadPerNode) if (mockNet.threadPerNode)
ServiceAffinityExecutor("Mock node $id thread", 1) ServiceAffinityExecutor("Mock node $id thread", 1)
@ -149,7 +148,15 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
// through the java.nio API which we are already mocking via Jimfs. // through the java.nio API which we are already mocking via Jimfs.
override fun makeMessagingService(): MessagingServiceInternal { override fun makeMessagingService(): MessagingServiceInternal {
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id } require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
return mockNet.messagingNetwork.createNodeWithID(!mockNet.threadPerNode, id, serverThread, makeServiceEntries(), configuration.myLegalName, database).start().getOrThrow() return mockNet.messagingNetwork.createNodeWithID(
!mockNet.threadPerNode,
id,
serverThread,
makeServiceEntries(),
configuration.myLegalName,
database)
.start()
.getOrThrow()
} }
override fun makeIdentityService() = MockIdentityService(mockNet.identities) override fun makeIdentityService() = MockIdentityService(mockNet.identities)
@ -165,7 +172,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
} }
override fun makeNetworkMapService() { override fun makeNetworkMapService() {
inNodeNetworkMapService = InMemoryNetworkMapService(services) inNodeNetworkMapService = InMemoryNetworkMapService(services, platformVersion)
} }
override fun makeServiceEntries(): List<ServiceEntry> { override fun makeServiceEntries(): List<ServiceEntry> {

View File

@ -21,7 +21,7 @@ import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.MOCK_VERSION import net.corda.testing.MOCK_VERSION_INFO
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
@ -68,7 +68,7 @@ open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub {
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException() override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
override val clock: Clock get() = Clock.systemUTC() override val clock: Clock get() = Clock.systemUTC()
override val schedulerService: SchedulerService get() = throw UnsupportedOperationException() override val schedulerService: SchedulerService get() = throw UnsupportedOperationException()
override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public), MOCK_VERSION) override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public), MOCK_VERSION_INFO.platformVersion)
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
fun makeVaultService(dataSourceProps: Properties): VaultService { fun makeVaultService(dataSourceProps: Properties): VaultService {

View File

@ -16,7 +16,7 @@ import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import net.corda.testing.MOCK_NODE_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.getFreeLocalPorts import net.corda.testing.getFreeLocalPorts
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.junit.After import org.junit.After
@ -141,7 +141,7 @@ abstract class NodeBasedTest {
) + configOverrides ) + configOverrides
) )
val node = config.parseAs<FullNodeConfiguration>().createNode(MOCK_NODE_VERSION_INFO) val node = config.parseAs<FullNodeConfiguration>().createNode(MOCK_VERSION_INFO)
node.start() node.start()
nodes += node nodes += node
thread(name = legalName) { thread(name = legalName) {

View File

@ -5,6 +5,7 @@ import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.testing.MOCK_VERSION_INFO
import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.RPCUserServiceImpl
import net.corda.node.services.api.MonitoringService import net.corda.node.services.api.MonitoringService
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
@ -13,7 +14,6 @@ import net.corda.node.services.messaging.NodeMessagingClient
import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.node.services.network.InMemoryNetworkMapCache
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.MOCK_NODE_VERSION_INFO
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
@ -38,7 +38,7 @@ class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeL
val net = database.transaction { val net = database.transaction {
NodeMessagingClient( NodeMessagingClient(
config, config,
MOCK_NODE_VERSION_INFO, MOCK_VERSION_INFO,
address, address,
identity.public, identity.public,
executor, executor,

View File

@ -80,9 +80,8 @@ dependencies {
jar { jar {
manifest { manifest {
attributes( attributes(
'Corda-Version': corda_version, 'Main-Class': mainClassName,
'Main-Class': mainClassName, 'Class-Path': configurations.compile.collect { it.getName() }.join(' ')
'Class-Path': configurations.compile.collect { it.getName() }.join(' ')
) )
} }
} }

View File

@ -31,12 +31,12 @@ dependencies {
task buildExplorerJAR(type: FatCapsule) { task buildExplorerJAR(type: FatCapsule) {
applicationClass 'net.corda.explorer.Main' applicationClass 'net.corda.explorer.Main'
archiveName "node-explorer-${corda_version}.jar" archiveName "node-explorer-${corda_release_version}.jar"
applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/ExplorerCaplet.class') applicationSource = files(project.tasks.findByName('jar'), '../build/classes/main/ExplorerCaplet.class')
classifier 'fat' classifier 'fat'
capsuleManifest { capsuleManifest {
applicationVersion = corda_version applicationVersion = corda_release_version
systemProperties['visualvm.display.name'] = 'Node Explorer' systemProperties['visualvm.display.name'] = 'Node Explorer'
minJavaVersion = '1.8.0' minJavaVersion = '1.8.0'
minUpdateVersion['1.8'] = java8_minUpdateVersion minUpdateVersion['1.8'] = java8_minUpdateVersion
@ -49,10 +49,6 @@ task buildExplorerJAR(type: FatCapsule) {
// If you change these flags, please also update Driver.kt // If you change these flags, please also update Driver.kt
jvmArgs = ['-Xmx200m', '-XX:+UseG1GC'] jvmArgs = ['-Xmx200m', '-XX:+UseG1GC']
} }
manifest {
attributes('Corda-Version': corda_version)
}
} }
build.dependsOn buildExplorerJAR build.dependsOn buildExplorerJAR

View File

@ -38,7 +38,7 @@ dependencies {
task buildWebserverJar(type: FatCapsule) { task buildWebserverJar(type: FatCapsule) {
applicationClass 'net.corda.webserver.WebServer' applicationClass 'net.corda.webserver.WebServer'
archiveName "corda-webserver-${corda_version}.jar" archiveName "corda-webserver-${corda_release_version}.jar"
applicationSource = files( applicationSource = files(
project.tasks.findByName('jar'), project.tasks.findByName('jar'),
new File(project(':node').rootDir, 'node/build/classes/main/CordaCaplet.class'), new File(project(':node').rootDir, 'node/build/classes/main/CordaCaplet.class'),
@ -48,10 +48,9 @@ task buildWebserverJar(type: FatCapsule) {
from 'NOTICE' // Copy CDDL notice from 'NOTICE' // Copy CDDL notice
capsuleManifest { capsuleManifest {
applicationVersion = corda_version applicationVersion = corda_release_version
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
systemProperties['visualvm.display.name'] = 'Corda Webserver' systemProperties['visualvm.display.name'] = 'Corda Webserver'
systemProperties['corda.version'] = corda_version
minJavaVersion = '1.8.0' minJavaVersion = '1.8.0'
minUpdateVersion['1.8'] = java8_minUpdateVersion minUpdateVersion['1.8'] = java8_minUpdateVersion
caplets = ['CordaCaplet'] caplets = ['CordaCaplet']
@ -63,10 +62,6 @@ task buildWebserverJar(type: FatCapsule) {
// If you change these flags, please also update Driver.kt // If you change these flags, please also update Driver.kt
jvmArgs = ['-Xmx200m', '-XX:+UseG1GC'] jvmArgs = ['-Xmx200m', '-XX:+UseG1GC']
} }
manifest {
attributes('Corda-Version': corda_version)
}
} }
artifacts { artifacts {