From 57f2be51b8f40cf47e32e1f585220ab907c3f07a Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 30 Nov 2016 08:30:40 +0000 Subject: [PATCH 01/37] Added core publishing. --- core/build.gradle | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index 6f7cc5512d..3364cca751 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'com.jfrog.bintray' buildscript { repositories { @@ -90,6 +91,26 @@ dependencies { compile "javax.ws.rs:javax.ws.rs-api:2.0" } +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['core'] + dryRun = false + pkg { + repo = 'corda' + name = 'core' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + publishing { publications { core(MavenPublication) { @@ -98,6 +119,34 @@ publishing { artifact sourceJar artifact javadocJar + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'core' + description 'Core core' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } } } } \ No newline at end of file From 6ecbe1f1fd00579797166126e72038ee4186be3a Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 30 Nov 2016 08:39:00 +0000 Subject: [PATCH 02/37] Added finance publishing. --- core/build.gradle | 1 + finance/build.gradle | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index 3364cca751..0381d88c47 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' buildscript { diff --git a/finance/build.gradle b/finance/build.gradle index 5ecc8f533c..a355793640 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -2,6 +2,8 @@ apply plugin: 'kotlin' apply plugin: CanonicalizerPlugin apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.quasar-utils' +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' repositories { mavenLocal() @@ -30,6 +32,26 @@ sourceSets { } } +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['finance'] + dryRun = false + pkg { + repo = 'corda' + name = 'finance' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + publishing { publications { finance(MavenPublication) { @@ -38,6 +60,34 @@ publishing { artifact sourceJar artifact javadocJar + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'finance' + description 'Corda finance modules' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } } } } From 77d433f73ad57e8b9acf3283799bbe0ea587ac77 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 30 Nov 2016 08:43:19 +0000 Subject: [PATCH 03/37] Added test-utils publishing. --- test-utils/build.gradle | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test-utils/build.gradle b/test-utils/build.gradle index 9736593c7e..12515b63e5 100644 --- a/test-utils/build.gradle +++ b/test-utils/build.gradle @@ -1,6 +1,8 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' repositories { mavenLocal() @@ -49,6 +51,26 @@ dependencies { compile 'com.squareup.okhttp3:okhttp:3.3.1' } +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['testutils'] + dryRun = false + pkg { + repo = 'corda' + name = 'test-utils' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + publishing { publications { testutils(MavenPublication) { @@ -57,6 +79,34 @@ publishing { artifact sourceJar artifact javadocJar + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'test-utils' + description 'Testing utilities for Corda' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } } } } From dec9cfc5a4d1ebdc94cc37dee427ead42c35faea Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 30 Nov 2016 08:48:16 +0000 Subject: [PATCH 04/37] Added client and node publishing. --- client/build.gradle | 74 +++++++++++++++++++++++++++++++++++++-------- node/build.gradle | 55 +++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 14 deletions(-) diff --git a/client/build.gradle b/client/build.gradle index bf04cfe540..8934e9867a 100644 --- a/client/build.gradle +++ b/client/build.gradle @@ -1,6 +1,8 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' repositories { mavenLocal() @@ -38,18 +40,6 @@ sourceSets { } } -publishing { - publications { - client(MavenPublication) { - from components.java - artifactId 'client' - - artifact sourceJar - artifact javadocJar - } - } -} - // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in // build/reports/project/dependencies/index.html for green highlighted parts of the tree. @@ -84,3 +74,63 @@ task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath } + +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['client'] + dryRun = false + pkg { + repo = 'corda' + name = 'client' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + +publishing { + publications { + client(MavenPublication) { + from components.java + artifactId 'client' + + artifact sourceJar + artifact javadocJar + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'client' + description 'Corda client modules' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } + } + } +} \ No newline at end of file diff --git a/node/build.gradle b/node/build.gradle index ed2cd6e5e6..2ac4ce7cff 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -3,6 +3,8 @@ apply plugin: 'java' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'us.kirchmeier.capsule' +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' repositories { mavenLocal() @@ -196,6 +198,27 @@ artifacts { runtimeArtifacts buildCordaJAR } + +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['node'] + dryRun = false + pkg { + repo = 'corda' + name = 'node' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + publishing { publications { node(MavenPublication) { @@ -204,7 +227,36 @@ publishing { artifact sourceJar artifact javadocJar + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'node' + description 'Corda node modules' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } } + corda(MavenPublication) { artifactId 'corda' @@ -212,6 +264,5 @@ publishing { classifier "" } } - } -} \ No newline at end of file +} From 39a34c7bf697fecbd59745b7dbbab49fc11db711 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 30 Nov 2016 13:51:41 +0000 Subject: [PATCH 05/37] Can now publish corda capsule to bintray --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index a16f42c558..f984177d43 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,6 @@ buildscript { // Our version: bump this on release. ext.corda_version = "0.7-SNAPSHOT" ext.gradle_plugins_version = "0.6.1" - ext.kotlin_version = '1.0.5' ext.quasar_version = '0.7.6' ext.asm_version = '0.5.3' From 348092cfa6c09072ce14a325ce23e968269cff14 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 1 Dec 2016 16:18:47 +0000 Subject: [PATCH 06/37] Re-added corda capsule publication. --- node/build.gradle | 50 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/node/build.gradle b/node/build.gradle index 2ac4ce7cff..03fdcf10b7 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -219,6 +219,26 @@ bintray { } } +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['corda'] + dryRun = false + pkg { + repo = 'corda' + name = 'corda' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + publishing { publications { node(MavenPublication) { @@ -263,6 +283,34 @@ publishing { artifact buildCordaJAR { classifier "" } + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'corda' + description 'Corda standalone node' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } } } -} +} \ No newline at end of file From 810596927e716efc1b4b70ff338d64eafbcc2b63 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 1 Dec 2016 16:34:25 +0000 Subject: [PATCH 07/37] Added a new capsule build.gradle to allow multiple bintray uploads from one build.gradle. --- build.gradle | 2 +- docs/source/example-code/build.gradle | 4 +- node/build.gradle | 94 ----------------- node/capsule/build.gradle | 127 +++++++++++++++++++++++ samples/attachment-demo/build.gradle | 2 +- samples/irs-demo/build.gradle | 2 +- samples/network-visualiser/build.gradle | 2 +- samples/raft-notary-demo/build.gradle | 2 +- samples/simm-valuation-demo/build.gradle | 2 +- samples/trader-demo/build.gradle | 2 +- settings.gradle | 1 + 11 files changed, 137 insertions(+), 103 deletions(-) create mode 100644 node/capsule/build.gradle diff --git a/build.gradle b/build.gradle index f984177d43..98f631836a 100644 --- a/build.gradle +++ b/build.gradle @@ -92,7 +92,7 @@ repositories { dependencies { compile project(':node') compile "com.google.guava:guava:19.0" - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') } task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle index 470ef52172..9bc0aeb012 100644 --- a/docs/source/example-code/build.gradle +++ b/docs/source/example-code/build.gradle @@ -36,7 +36,7 @@ sourceSets { } } -compileTestJava.dependsOn tasks.getByPath(':node:buildCordaJAR') +compileTestJava.dependsOn tasks.getByPath(':node:capsule:buildCordaJAR') dependencies { compile project(':core') @@ -48,7 +48,7 @@ dependencies { exclude group: "bouncycastle" } - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') } mainClassName = "net.corda.docs.ClientRpcTutorialKt" diff --git a/node/build.gradle b/node/build.gradle index 03fdcf10b7..2d962ed1ab 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -2,7 +2,6 @@ apply plugin: 'kotlin' apply plugin: 'java' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'us.kirchmeier.capsule' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' @@ -26,18 +25,8 @@ configurations { integrationTestCompile.extendsFrom testCompile integrationTestRuntime.extendsFrom testRuntime - - runtimeArtifacts.extendsFrom runtime } -// Force the Caplet to target Java 6. This ensures that running 'java -jar corda.jar' on any Java 6 VM upwards -// will get as far as the Capsule version checks, meaning that if your JVM is too old, you will at least get -// a sensible error message telling you what to do rather than a bytecode version exception that doesn't. -// If we introduce .java files into this module that need Java 8+ then we will have to push the caplet into -// its own module so its target can be controlled individually, but for now this suffices. -sourceCompatibility = 1.6 -targetCompatibility = 1.6 - sourceSets { integrationTest { kotlin { @@ -172,33 +161,6 @@ task integrationTest(type: Test) { classpath = sourceSets.integrationTest.runtimeClasspath } -task buildCordaJAR(type: FatCapsule, dependsOn: ['jar', 'buildCertSigningRequestUtilityJAR']) { - applicationClass 'net.corda.node.MainKt' - archiveName "corda-${corda_version}.jar" - applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class', 'config/dev/log4j2.xml') - - capsuleManifest { - appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] - javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] - minJavaVersion = '1.8.0' - caplets = ['CordaCaplet'] - } -} - -task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: project.jar) { - applicationClass 'net.corda.node.utilities.certsigning.CertificateSignerKt' - archiveName 'certSigningRequestUtility.jar' - capsuleManifest { - systemProperties['log4j.configuration'] = 'log4j2.xml' - minJavaVersion = '1.8.0' - } -} - -artifacts { - runtimeArtifacts buildCordaJAR -} - - bintray { user = System.getenv('CORDA_BINTRAY_USER') key = System.getenv('CORDA_BINTRAY_KEY') @@ -219,26 +181,6 @@ bintray { } } -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['corda'] - dryRun = false - pkg { - repo = 'corda' - name = 'corda' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - publishing { publications { node(MavenPublication) { @@ -276,41 +218,5 @@ publishing { } } } - - corda(MavenPublication) { - artifactId 'corda' - - artifact buildCordaJAR { - classifier "" - } - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'corda' - description 'Corda standalone node' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } } } \ No newline at end of file diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle new file mode 100644 index 0000000000..063eeca767 --- /dev/null +++ b/node/capsule/build.gradle @@ -0,0 +1,127 @@ +/** + * This build.gradle exists to publish our capsule (executable fat jar) to maven. It cannot be placed in the + * node project because the bintray plugin cannot publish two modules from one project. + */ +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'us.kirchmeier.capsule' +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' + +repositories { + mavenCentral() +} + +configurations { + runtimeArtifacts.extendsFrom runtime +} + +// Force the Caplet to target Java 6. This ensures that running 'java -jar corda.jar' on any Java 6 VM upwards +// will get as far as the Capsule version checks, meaning that if your JVM is too old, you will at least get +// a sensible error message telling you what to do rather than a bytecode version exception that doesn't. +// If we introduce .java files into this module that need Java 8+ then we will have to push the caplet into +// its own module so its target can be controlled individually, but for now this suffices. +sourceCompatibility = 1.6 +targetCompatibility = 1.6 + +sourceSets { + test { + resources { + srcDir "../config/test" + } + } + main { + resources { + srcDir "../config/dev" + } + } +} + +dependencies { + compile project(':node') +} + +task buildCordaJAR(type: FatCapsule, dependsOn: ['jar', 'buildCertSigningRequestUtilityJAR']) { + applicationClass 'net.corda.node.MainKt' + archiveName "corda-${corda_version}.jar" + applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class', 'config/dev/log4j2.xml') + + capsuleManifest { + appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] + javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] + minJavaVersion = '1.8.0' + caplets = ['CordaCaplet'] + } +} + +task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: project.jar) { + applicationClass 'net.corda.node.utilities.certsigning.CertificateSignerKt' + archiveName 'certSigningRequestUtility.jar' + capsuleManifest { + systemProperties['log4j.configuration'] = 'log4j2.xml' + minJavaVersion = '1.8.0' + } +} + +artifacts { + runtimeArtifacts buildCordaJAR +} + +bintray { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + publications = ['corda'] + dryRun = false + pkg { + repo = 'corda' + name = 'corda' + userOrg = 'r3' + licenses = ['Apache-2.0'] + + version { + gpg { + sign = true + passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + } + } + } +} + +publishing { + publications { + corda(MavenPublication) { + artifactId 'corda' + + artifact buildCordaJAR { + classifier "" + } + + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name 'corda' + description 'Corda standalone node' + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } + } + } +} \ No newline at end of file diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index a0f09c3d9e..89991bc556 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 27d3cd3ede..0400939e62 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -48,7 +48,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/network-visualiser/build.gradle b/samples/network-visualiser/build.gradle index fcfbfbde06..5a0169a4ec 100644 --- a/samples/network-visualiser/build.gradle +++ b/samples/network-visualiser/build.gradle @@ -21,7 +21,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/raft-notary-demo/build.gradle b/samples/raft-notary-demo/build.gradle index 9493636407..c0ed340679 100644 --- a/samples/raft-notary-demo/build.gradle +++ b/samples/raft-notary-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 23c54c3359..d444858e82 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -41,7 +41,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index c1be618df3..403f5b879b 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/settings.gradle b/settings.gradle index 89b4247424..b63878100e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ include 'finance' include 'finance:isolated' include 'core' include 'node' +include 'node:capsule' include 'client' include 'experimental' include 'experimental:sandbox' From c8e14b0ac6c53a00d4da13b3e116a284dfbb00d7 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 1 Dec 2016 17:28:13 +0000 Subject: [PATCH 08/37] Added all artifacts required for the corda capsule to be published on maven central. --- node/capsule/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index 063eeca767..22a9b625d7 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -8,7 +8,15 @@ apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' repositories { + mavenLocal() mavenCentral() + maven { + url 'http://oss.sonatype.org/content/repositories/snapshots' + } + jcenter() + maven { + url 'https://dl.bintray.com/kotlin/exposed' + } } configurations { @@ -91,6 +99,8 @@ publishing { corda(MavenPublication) { artifactId 'corda' + artifact sourceJar + artifact javadocJar artifact buildCordaJAR { classifier "" } From 4eb7d3db1156d64fdf62bc2ec003824e482ae5f6 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 1 Dec 2016 20:24:04 +0000 Subject: [PATCH 09/37] Added more publish utilities for simplifying bintray configuration per project to reduced boilerplate. --- gradle-plugins/build.gradle | 16 +++++- gradle-plugins/cordformation/build.gradle | 19 +------ .../plugins/BintrayConfigExtension.groovy | 11 ++++ .../plugins/BintrayPublishExtension.groovy | 7 +++ .../net/corda/plugins/PublishTasks.groovy | 52 ++++++++++++++++--- gradle-plugins/quasar-utils/build.gradle | 19 +------ gradle-plugins/settings.gradle | 2 +- 7 files changed, 83 insertions(+), 43 deletions(-) create mode 100644 gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy create mode 100644 gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index f80527f8c5..6ca6560323 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -2,10 +2,11 @@ // or if you are developing these plugins. See the readme for more information. buildscript { - ext.gradle_plugins_version = "0.6.1" // Our version: bump this on release. - ext.corda_published_version = "0.5" // Depend on our existing published publishing plugin. + ext.gradle_plugins_version = "0.6.2" // Our version: bump this on release. + ext.corda_published_version = "0.6.2" // Depend on our existing published publishing plugin. repositories { + mavenLocal() jcenter() } @@ -16,6 +17,7 @@ buildscript { } apply plugin: 'maven-publish' +apply plugin: 'net.corda.plugins.publish-utils' allprojects { version "$gradle_plugins_version" @@ -26,6 +28,16 @@ subprojects { task(install, dependsOn: 'publishToMavenLocal') } +bintrayConfig { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + repo = 'corda' + org = 'r3' + licenses = ['Apache-2.0'] + gpgSign = true + gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') +} + // Aliasing the publishToMavenLocal for simplicity. task(install, dependsOn: 'publishToMavenLocal') diff --git a/gradle-plugins/cordformation/build.gradle b/gradle-plugins/cordformation/build.gradle index 9ae980c4b1..589a4027ce 100644 --- a/gradle-plugins/cordformation/build.gradle +++ b/gradle-plugins/cordformation/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'groovy' apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' dependencies { compile gradleApi() @@ -14,24 +13,10 @@ repositories { mavenCentral() } -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') +bintrayPublish { + name = 'cordformation' publications = ['cordformation'] dryRun = false - pkg { - repo = 'corda' - name = 'cordformation' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } } publishing { diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy new file mode 100644 index 0000000000..4f189b978b --- /dev/null +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy @@ -0,0 +1,11 @@ +package net.corda.plugins + +class BintrayConfigExtension { + String user + String key + String repo + String org + String[] licenses + Boolean gpgSign + String gpgPassphrase +} \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy new file mode 100644 index 0000000000..927d411877 --- /dev/null +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy @@ -0,0 +1,7 @@ +package net.corda.plugins + +class BintrayPublishExtension { + String name + Boolean dryRun + String[] publications +} \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index c2d38e1f81..ed8c579057 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -7,17 +7,57 @@ import org.gradle.api.Project /** * A utility plugin that when applied will automatically create source and javadoc publishing tasks + * To apply this plugin you must also add 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' to your + * buildscript's classpath dependencies. */ class PublishTasks implements Plugin { void apply(Project project) { - project.task("sourceJar", type: Jar, dependsOn: project.classes) { - classifier = 'sources' - from project.sourceSets.main.allSource + if(project.hasProperty('classes')) { + project.task("sourceJar", type: Jar, dependsOn: project.classes) { + classifier = 'sources' + from project.sourceSets.main.allSource + } } - project.task("javadocJar", type: Jar, dependsOn: project.javadoc) { - classifier = 'javadoc' - from project.javadoc.destinationDir + if(project.hasProperty('javadoc')) { + project.task("javadocJar", type: Jar, dependsOn: project.javadoc) { + classifier = 'javadoc' + from project.javadoc.destinationDir + } + } + + project.extensions.create("bintrayConfig", BintrayConfigExtension) + project.extensions.create("bintrayPublish", BintrayPublishExtension) + + def bintrayValues = project.extensions.findByName("bintrayPublish") + def bintrayConfig = project.rootProject.extensions.findByName('bintrayConfig') + if((bintrayConfig != null) && (bintrayValues != null)) { + // TODO AM: + // Problem 1. Bootstrapping - do not want root to depend on this project + // Problem 2. This project's extension is not available here + // Problem 3. Bintray's extension is already configured after evaluation + // Possible solutions: + // name: project.name + // publications: project.name (make it a forced convention) + // dryRun: move to root. + // Problem 4: Root project therefore cannot be published + // Solution: Why use this plugin if you only have a root project? + project.configure(project) { + apply plugin: 'com.jfrog.bintray' + } + def bintray = project.extensions.findByName("bintray") + + project.logger.info("Configuring bintray for ${project.name}") + bintray.user = bintrayConfig.user + bintray.key = bintrayConfig.key + bintray.publications = bintrayValues.publications + bintray.dryRun = bintrayValues.dryRun ?: false + bintray.pkg.repo = bintrayConfig.repo + bintray.pkg.name = bintrayValues.name ?: project.name + bintray.pkg.userOrg = bintrayConfig.org + bintray.pkg.licenses = bintrayConfig.licenses + bintray.pkg.version.gpg.sign = bintrayConfig.gpgSign ?: false + bintray.pkg.version.gpg.passphrase = bintrayConfig.gpgPassphrase } } } diff --git a/gradle-plugins/quasar-utils/build.gradle b/gradle-plugins/quasar-utils/build.gradle index 32ebb113c9..7e865d7701 100644 --- a/gradle-plugins/quasar-utils/build.gradle +++ b/gradle-plugins/quasar-utils/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'groovy' apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'com.jfrog.bintray' apply plugin: 'maven-publish' dependencies { @@ -12,24 +11,10 @@ repositories { mavenCentral() } -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') +bintrayPublish { + name = 'quasar-utils' publications = ['quasarUtils'] dryRun = false - pkg { - repo = 'corda' - name = 'quasar-utils' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } } publishing { diff --git a/gradle-plugins/settings.gradle b/gradle-plugins/settings.gradle index 77b6f8f6b4..50e3950b93 100644 --- a/gradle-plugins/settings.gradle +++ b/gradle-plugins/settings.gradle @@ -1,4 +1,4 @@ rootProject.name = 'corda-gradle-plugins' -include 'quasar-utils' include 'publish-utils' +include 'quasar-utils' include 'cordformation' \ No newline at end of file From 46ddd3bd0e35b0da05eed2e0230fec3db9eabf62 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 2 Dec 2016 12:00:25 +0000 Subject: [PATCH 10/37] Publish utils no longer needs a configuration block per project, just one bintray configuration per project providing it fits certain criteria. --- gradle-plugins/build.gradle | 1 + gradle-plugins/cordformation/build.gradle | 6 ------ .../plugins/BintrayConfigExtension.groovy | 2 ++ .../plugins/BintrayPublishExtension.groovy | 7 ------- .../net/corda/plugins/PublishTasks.groovy | 21 +++++-------------- gradle-plugins/quasar-utils/build.gradle | 6 ------ 6 files changed, 8 insertions(+), 35 deletions(-) delete mode 100644 gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index 6ca6560323..a0b34e8bf1 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -36,6 +36,7 @@ bintrayConfig { licenses = ['Apache-2.0'] gpgSign = true gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + publications = ['cordformation', 'quasar-utils'] } // Aliasing the publishToMavenLocal for simplicity. diff --git a/gradle-plugins/cordformation/build.gradle b/gradle-plugins/cordformation/build.gradle index 589a4027ce..1a650e0bd0 100644 --- a/gradle-plugins/cordformation/build.gradle +++ b/gradle-plugins/cordformation/build.gradle @@ -13,12 +13,6 @@ repositories { mavenCentral() } -bintrayPublish { - name = 'cordformation' - publications = ['cordformation'] - dryRun = false -} - publishing { publications { cordformation(MavenPublication) { diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy index 4f189b978b..6f703ce4d9 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy @@ -8,4 +8,6 @@ class BintrayConfigExtension { String[] licenses Boolean gpgSign String gpgPassphrase + String[] publications + Boolean dryRun } \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy deleted file mode 100644 index 927d411877..0000000000 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayPublishExtension.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package net.corda.plugins - -class BintrayPublishExtension { - String name - Boolean dryRun - String[] publications -} \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index ed8c579057..7353ac4d6d 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -27,33 +27,22 @@ class PublishTasks implements Plugin { } project.extensions.create("bintrayConfig", BintrayConfigExtension) - project.extensions.create("bintrayPublish", BintrayPublishExtension) - def bintrayValues = project.extensions.findByName("bintrayPublish") def bintrayConfig = project.rootProject.extensions.findByName('bintrayConfig') - if((bintrayConfig != null) && (bintrayValues != null)) { - // TODO AM: - // Problem 1. Bootstrapping - do not want root to depend on this project - // Problem 2. This project's extension is not available here - // Problem 3. Bintray's extension is already configured after evaluation - // Possible solutions: - // name: project.name - // publications: project.name (make it a forced convention) - // dryRun: move to root. - // Problem 4: Root project therefore cannot be published - // Solution: Why use this plugin if you only have a root project? + if((bintrayConfig != null) && (bintrayConfig.publications)) { project.configure(project) { apply plugin: 'com.jfrog.bintray' } def bintray = project.extensions.findByName("bintray") + println(bintrayConfig.publications.findAll { it == project.name }) project.logger.info("Configuring bintray for ${project.name}") bintray.user = bintrayConfig.user bintray.key = bintrayConfig.key - bintray.publications = bintrayValues.publications - bintray.dryRun = bintrayValues.dryRun ?: false + bintray.publications = bintrayConfig.publications.findAll { it == project.name } + bintray.dryRun = bintrayConfig.dryRun ?: false bintray.pkg.repo = bintrayConfig.repo - bintray.pkg.name = bintrayValues.name ?: project.name + bintray.pkg.name = project.name bintray.pkg.userOrg = bintrayConfig.org bintray.pkg.licenses = bintrayConfig.licenses bintray.pkg.version.gpg.sign = bintrayConfig.gpgSign ?: false diff --git a/gradle-plugins/quasar-utils/build.gradle b/gradle-plugins/quasar-utils/build.gradle index 7e865d7701..ee31ed4a1f 100644 --- a/gradle-plugins/quasar-utils/build.gradle +++ b/gradle-plugins/quasar-utils/build.gradle @@ -11,12 +11,6 @@ repositories { mavenCentral() } -bintrayPublish { - name = 'quasar-utils' - publications = ['quasarUtils'] - dryRun = false -} - publishing { publications { quasarUtils(MavenPublication) { From 6bc118b9974bce2bb899caabb6f93a87176244cf Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 2 Dec 2016 12:05:25 +0000 Subject: [PATCH 11/37] Quasar utils now follows the format required by the new publishing utilities gradle plugin. --- gradle-plugins/quasar-utils/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugins/quasar-utils/build.gradle b/gradle-plugins/quasar-utils/build.gradle index ee31ed4a1f..d66ccc9034 100644 --- a/gradle-plugins/quasar-utils/build.gradle +++ b/gradle-plugins/quasar-utils/build.gradle @@ -13,7 +13,7 @@ repositories { publishing { publications { - quasarUtils(MavenPublication) { + "quasar-utils"(MavenPublication) { from components.java groupId 'net.corda.plugins' artifactId 'quasar-utils' From e751d80c00297de62f064c46dd4e5718b23729ed Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 2 Dec 2016 17:39:37 +0000 Subject: [PATCH 12/37] Added documentation for bintray configuration. --- .../plugins/BintrayConfigExtension.groovy | 31 +++++++++++++++++++ .../net/corda/plugins/PublishTasks.groovy | 3 ++ 2 files changed, 34 insertions(+) diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy index 6f703ce4d9..e6c6ec0809 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy @@ -1,13 +1,44 @@ package net.corda.plugins class BintrayConfigExtension { + /** + * Bintray username + */ String user + /** + * Bintray access key + */ String key + /** + * Bintray repository + */ String repo + /** + * Bintray organisation + */ String org + /** + * Licenses for packages uploaded by this configuration + */ String[] licenses + /** + * Whether to sign packages uploaded by this configuration + */ Boolean gpgSign + /** + * The passphrase for the key used to sign releases. + */ String gpgPassphrase + /** + * The publications that will be uploaded as a part of this configuration. These must match both the name on + * bintray and the gradle module name. ie; it must be "some-package" as a gradle sub-module (root project not + * supported, this extension is to improve multi-build bintray uploads). The publication must also be called + * "some-package". Only one publication can be uploaded per module (a bintray plugin restriction(. + * If any of these conditions are not met your package will not be uploaded. + */ String[] publications + /** + * Whether to test the publication without uploading to bintray. + */ Boolean dryRun } \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index 7353ac4d6d..97607ab21f 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -9,6 +9,9 @@ import org.gradle.api.Project * A utility plugin that when applied will automatically create source and javadoc publishing tasks * To apply this plugin you must also add 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' to your * buildscript's classpath dependencies. + * + * To use this plugin you can add a new configuration block (extension) to your root build.gradle. See the fields + * in BintrayConfigExtension. */ class PublishTasks implements Plugin { void apply(Project project) { From 6ac7b9384da1d8ffeec3efca953bf05fc5549175 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Mon, 5 Dec 2016 17:37:37 +0000 Subject: [PATCH 13/37] All gradle plugins can now be published with the publishing plugin. --- gradle-plugins/build.gradle | 2 +- gradle-plugins/cordformation/build.gradle | 52 ++------- .../net/corda/plugins/PublishTasks.groovy | 104 ++++++++++++++---- gradle-plugins/quasar-utils/build.gradle | 51 +-------- 4 files changed, 97 insertions(+), 112 deletions(-) diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index a0b34e8bf1..2ea7adb464 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -21,7 +21,7 @@ apply plugin: 'net.corda.plugins.publish-utils' allprojects { version "$gradle_plugins_version" - group 'net.corda' + group 'net.corda.plugins' } subprojects { diff --git a/gradle-plugins/cordformation/build.gradle b/gradle-plugins/cordformation/build.gradle index 1a650e0bd0..26a7b9c198 100644 --- a/gradle-plugins/cordformation/build.gradle +++ b/gradle-plugins/cordformation/build.gradle @@ -1,6 +1,12 @@ apply plugin: 'groovy' -apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' +apply plugin: 'net.corda.plugins.publish-utils' + +description 'A small gradle plugin for adding some basic Quasar tasks and configurations to reduce build.gradle bloat.' + +repositories { + mavenCentral() +} dependencies { compile gradleApi() @@ -9,47 +15,3 @@ dependencies { compile "com.typesafe:config:1.3.0" } -repositories { - mavenCentral() -} - -publishing { - publications { - cordformation(MavenPublication) { - from components.java - groupId 'net.corda.plugins' - artifactId 'cordformation' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'cordformation' - description 'A small gradle plugin for adding some basic Quasar tasks and configurations to reduce build.gradle bloat.' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } -} diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index 97607ab21f..497dfb624f 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -4,6 +4,8 @@ import org.gradle.api.* import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.api.Project +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.maven.MavenPom /** * A utility plugin that when applied will automatically create source and javadoc publishing tasks @@ -14,7 +16,84 @@ import org.gradle.api.Project * in BintrayConfigExtension. */ class PublishTasks implements Plugin { + Project project + void apply(Project project) { + this.project = project + + createTasks() + createExtensions() + + def bintrayConfig = project.rootProject.extensions.findByName('bintrayConfig') + if((bintrayConfig != null) && (bintrayConfig.publications)) { + def publications = bintrayConfig.publications.findAll { it == project.name } + println("HI") + if(publications.size > 0) { + project.logger.info("Configuring bintray for ${project.name}") + project.configure(project) { + apply plugin: 'com.jfrog.bintray' + } + def bintray = project.extensions.findByName("bintray") + configureBintray(bintray, bintrayConfig) + project.publishing.publications.create(project.name, MavenPublication) { + from project.components.java + groupId project.group + artifactId project.name + + artifact project.tasks.sourceJar + artifact project.tasks.javadocJar + + extendPomForMavenCentral(pom) + } + } + } + } + + // Maven central requires all of the below fields for this to be a valid POM + void extendPomForMavenCentral(MavenPom pom) { + pom.withXml { + asNode().children().last() + { + resolveStrategy = Closure.DELEGATE_FIRST + name project.name + description project.description + url 'https://github.com/corda/corda' + scm { + url 'https://github.com/corda/corda' + } + + licenses { + license { + name 'Apache-2.0' + url 'https://www.apache.org/licenses/LICENSE-2.0' + distribution 'repo' + } + } + + developers { + developer { + id 'R3' + name 'R3' + email 'dev@corda.net' + } + } + } + } + } + + void configureBintray(def bintray, def bintrayConfig) { + bintray.user = bintrayConfig.user + bintray.key = bintrayConfig.key + bintray.publications = [ project.name ] + bintray.dryRun = bintrayConfig.dryRun ?: false + bintray.pkg.repo = bintrayConfig.repo + bintray.pkg.name = project.name + bintray.pkg.userOrg = bintrayConfig.org + bintray.pkg.licenses = bintrayConfig.licenses + bintray.pkg.version.gpg.sign = bintrayConfig.gpgSign ?: false + bintray.pkg.version.gpg.passphrase = bintrayConfig.gpgPassphrase + } + + void createTasks() { if(project.hasProperty('classes')) { project.task("sourceJar", type: Jar, dependsOn: project.classes) { classifier = 'sources' @@ -28,28 +107,11 @@ class PublishTasks implements Plugin { from project.javadoc.destinationDir } } + } - project.extensions.create("bintrayConfig", BintrayConfigExtension) - - def bintrayConfig = project.rootProject.extensions.findByName('bintrayConfig') - if((bintrayConfig != null) && (bintrayConfig.publications)) { - project.configure(project) { - apply plugin: 'com.jfrog.bintray' - } - def bintray = project.extensions.findByName("bintray") - println(bintrayConfig.publications.findAll { it == project.name }) - - project.logger.info("Configuring bintray for ${project.name}") - bintray.user = bintrayConfig.user - bintray.key = bintrayConfig.key - bintray.publications = bintrayConfig.publications.findAll { it == project.name } - bintray.dryRun = bintrayConfig.dryRun ?: false - bintray.pkg.repo = bintrayConfig.repo - bintray.pkg.name = project.name - bintray.pkg.userOrg = bintrayConfig.org - bintray.pkg.licenses = bintrayConfig.licenses - bintray.pkg.version.gpg.sign = bintrayConfig.gpgSign ?: false - bintray.pkg.version.gpg.passphrase = bintrayConfig.gpgPassphrase + void createExtensions() { + if(project == project.rootProject) { + project.extensions.create("bintrayConfig", BintrayConfigExtension) } } } diff --git a/gradle-plugins/quasar-utils/build.gradle b/gradle-plugins/quasar-utils/build.gradle index d66ccc9034..ad4a10ecff 100644 --- a/gradle-plugins/quasar-utils/build.gradle +++ b/gradle-plugins/quasar-utils/build.gradle @@ -1,53 +1,14 @@ apply plugin: 'groovy' -apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' +apply plugin: 'net.corda.plugins.publish-utils' -dependencies { - compile gradleApi() - compile localGroovy() -} +description 'A small gradle plugin for adding some basic Quasar tasks and configurations to reduce build.gradle bloat.' repositories { mavenCentral() } -publishing { - publications { - "quasar-utils"(MavenPublication) { - from components.java - groupId 'net.corda.plugins' - artifactId 'quasar-utils' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'quasar-utils' - description 'A small gradle plugin for adding some basic Quasar tasks and configurations to reduce build.gradle bloat.' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } -} +dependencies { + compile gradleApi() + compile localGroovy() +} \ No newline at end of file From 4b2d7867249b9270b1dca495b56d2b7758ee3b8a Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 10:05:02 +0000 Subject: [PATCH 14/37] Bintray configuration block now contains all fields required for the bare minimum POM. --- gradle-plugins/build.gradle | 12 ++++++++ .../net/corda/plugins/PublishTasks.groovy | 26 ++++++++--------- .../BintrayConfigExtension.groovy | 28 ++++++++++++++++++- .../corda/plugins/bintray/Developer.groovy | 7 +++++ .../net/corda/plugins/bintray/License.groovy | 7 +++++ 5 files changed, 66 insertions(+), 14 deletions(-) rename gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/{ => bintray}/BintrayConfigExtension.groovy (66%) create mode 100644 gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy create mode 100644 gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index 2ea7adb464..7a15cb8840 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -34,9 +34,21 @@ bintrayConfig { repo = 'corda' org = 'r3' licenses = ['Apache-2.0'] + vcsUrl = 'https://github.com/corda/corda' + projectUrl = 'https://github.com/corda/corda' gpgSign = true gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') publications = ['cordformation', 'quasar-utils'] + license { + name = 'Apache-2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0' + distribution = 'repo' + } + developer { + id = 'R3' + name = 'R3' + email = 'dev@corda.net' + } } // Aliasing the publishToMavenLocal for simplicity. diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index 497dfb624f..f54c1a4f11 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -6,6 +6,7 @@ import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.api.Project import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.MavenPom +import net.corda.plugins.bintray.* /** * A utility plugin that when applied will automatically create source and javadoc publishing tasks @@ -24,10 +25,9 @@ class PublishTasks implements Plugin { createTasks() createExtensions() - def bintrayConfig = project.rootProject.extensions.findByName('bintrayConfig') + def bintrayConfig = project.rootProject.extensions.findByType(BintrayConfigExtension.class) if((bintrayConfig != null) && (bintrayConfig.publications)) { def publications = bintrayConfig.publications.findAll { it == project.name } - println("HI") if(publications.size > 0) { project.logger.info("Configuring bintray for ${project.name}") project.configure(project) { @@ -43,44 +43,44 @@ class PublishTasks implements Plugin { artifact project.tasks.sourceJar artifact project.tasks.javadocJar - extendPomForMavenCentral(pom) + extendPomForMavenCentral(pom, bintrayConfig) } } } } // Maven central requires all of the below fields for this to be a valid POM - void extendPomForMavenCentral(MavenPom pom) { + void extendPomForMavenCentral(MavenPom pom, BintrayConfigExtension config) { pom.withXml { asNode().children().last() + { resolveStrategy = Closure.DELEGATE_FIRST name project.name description project.description - url 'https://github.com/corda/corda' + url config.projectUrl scm { - url 'https://github.com/corda/corda' + url config.vcsUrl } licenses { license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' + name config.license.name + url config.license.url + distribution config.license.url } } developers { developer { - id 'R3' - name 'R3' - email 'dev@corda.net' + id config.developer.id + name config.developer.name + email config.developer.email } } } } } - void configureBintray(def bintray, def bintrayConfig) { + void configureBintray(def bintray, BintrayConfigExtension bintrayConfig) { bintray.user = bintrayConfig.user bintray.key = bintrayConfig.key bintray.publications = [ project.name ] diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/BintrayConfigExtension.groovy similarity index 66% rename from gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy rename to gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/BintrayConfigExtension.groovy index e6c6ec0809..1a1c4e49e5 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/BintrayConfigExtension.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/BintrayConfigExtension.groovy @@ -1,4 +1,6 @@ -package net.corda.plugins +package net.corda.plugins.bintray + +import org.gradle.util.ConfigureUtil class BintrayConfigExtension { /** @@ -29,6 +31,14 @@ class BintrayConfigExtension { * The passphrase for the key used to sign releases. */ String gpgPassphrase + /** + * VCS URL + */ + String vcsUrl + /** + * Project URL + */ + String projectUrl /** * The publications that will be uploaded as a part of this configuration. These must match both the name on * bintray and the gradle module name. ie; it must be "some-package" as a gradle sub-module (root project not @@ -41,4 +51,20 @@ class BintrayConfigExtension { * Whether to test the publication without uploading to bintray. */ Boolean dryRun + /** + * The license this project will use (currently limited to one) + */ + License license = new License() + /** + * The developer of this project (currently limited to one) + */ + Developer developer = new Developer() + + void license(Closure closure) { + ConfigureUtil.configure(closure, license) + } + + void developer(Closure closure) { + ConfigureUtil.configure(closure, developer) + } } \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy new file mode 100644 index 0000000000..1f8df63ece --- /dev/null +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy @@ -0,0 +1,7 @@ +package net.corda.plugins.bintray + +class Developer { + String id + String name + String email +} \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy new file mode 100644 index 0000000000..ca03d4ade4 --- /dev/null +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy @@ -0,0 +1,7 @@ +package net.corda.plugins.bintray + +class License { + String name + String url + String distribution +} \ No newline at end of file From bf49dda731b82f05d2b1768b5f0a55b2ea32f38d Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 10:49:46 +0000 Subject: [PATCH 15/37] Corda now publishes with updated publish utils. --- build.gradle | 26 +++++++- client/build.gradle | 62 +----------------- core/build.gradle | 64 +----------------- finance/build.gradle | 64 +----------------- gradle-plugins/cordformation/build.gradle | 1 - .../net/corda/plugins/PublishTasks.groovy | 41 +++++++----- node/build.gradle | 62 +----------------- node/capsule/build.gradle | 65 +------------------ test-utils/build.gradle | 64 +----------------- 9 files changed, 64 insertions(+), 385 deletions(-) diff --git a/build.gradle b/build.gradle index 98f631836a..81fe22c346 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { // Our version: bump this on release. ext.corda_version = "0.7-SNAPSHOT" - ext.gradle_plugins_version = "0.6.1" + ext.gradle_plugins_version = "0.6.2" ext.kotlin_version = '1.0.5' ext.quasar_version = '0.7.6' ext.asm_version = '0.5.3' @@ -45,6 +45,7 @@ apply plugin: 'kotlin' apply plugin: 'project-report' apply plugin: 'com.github.ben-manes.versions' apply plugin: 'maven-publish' +apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordformation' @@ -154,3 +155,26 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) { // Aliasing the publishToMavenLocal for simplicity. task(install, dependsOn: 'publishToMavenLocal') + +bintrayConfig { + user = System.getenv('CORDA_BINTRAY_USER') + key = System.getenv('CORDA_BINTRAY_KEY') + repo = 'corda' + org = 'r3' + licenses = ['Apache-2.0'] + vcsUrl = 'https://github.com/corda/corda' + projectUrl = 'https://github.com/corda/corda' + gpgSign = true + gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') + publications = ['client', 'core', 'finance', 'node', 'test-utils'] + license { + name = 'Apache-2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0' + distribution = 'repo' + } + developer { + id = 'R3' + name = 'R3' + email = 'dev@corda.net' + } +} \ No newline at end of file diff --git a/client/build.gradle b/client/build.gradle index 8934e9867a..4d2780e9bf 100644 --- a/client/build.gradle +++ b/client/build.gradle @@ -4,6 +4,8 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +description 'Corda client modules' + repositories { mavenLocal() mavenCentral() @@ -73,64 +75,4 @@ dependencies { task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath -} - -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['client'] - dryRun = false - pkg { - repo = 'corda' - name = 'client' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - -publishing { - publications { - client(MavenPublication) { - from components.java - artifactId 'client' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'client' - description 'Corda client modules' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } } \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle index 0381d88c47..67bf903409 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -4,14 +4,14 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +description 'Corda core' + buildscript { repositories { mavenCentral() } } -// apply plugin: 'org.jetbrains.dokka' - repositories { mavenLocal() mavenCentral() @@ -90,64 +90,4 @@ dependencies { // RS API: Response type and codes for ApiUtils. compile "javax.ws.rs:javax.ws.rs-api:2.0" -} - -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['core'] - dryRun = false - pkg { - repo = 'corda' - name = 'core' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - -publishing { - publications { - core(MavenPublication) { - from components.java - artifactId 'core' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'core' - description 'Core core' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } } \ No newline at end of file diff --git a/finance/build.gradle b/finance/build.gradle index a355793640..699f18a92e 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -5,6 +5,8 @@ apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +description 'Corda finance modules' + repositories { mavenLocal() mavenCentral() @@ -30,64 +32,4 @@ sourceSets { srcDir "../config/test" } } -} - -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['finance'] - dryRun = false - pkg { - repo = 'corda' - name = 'finance' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - -publishing { - publications { - finance(MavenPublication) { - from components.java - artifactId 'finance' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'finance' - description 'Corda finance modules' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } -} +} \ No newline at end of file diff --git a/gradle-plugins/cordformation/build.gradle b/gradle-plugins/cordformation/build.gradle index 26a7b9c198..80b92a58dc 100644 --- a/gradle-plugins/cordformation/build.gradle +++ b/gradle-plugins/cordformation/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'groovy' -apply plugin: 'maven-publish' apply plugin: 'net.corda.plugins.publish-utils' description 'A small gradle plugin for adding some basic Quasar tasks and configurations to reduce build.gradle bloat.' diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index f54c1a4f11..ff59810e44 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -24,27 +24,34 @@ class PublishTasks implements Plugin { createTasks() createExtensions() + checkAndApplyPublishing() + } + void checkAndApplyPublishing() { def bintrayConfig = project.rootProject.extensions.findByType(BintrayConfigExtension.class) - if((bintrayConfig != null) && (bintrayConfig.publications)) { - def publications = bintrayConfig.publications.findAll { it == project.name } - if(publications.size > 0) { - project.logger.info("Configuring bintray for ${project.name}") - project.configure(project) { - apply plugin: 'com.jfrog.bintray' - } - def bintray = project.extensions.findByName("bintray") - configureBintray(bintray, bintrayConfig) - project.publishing.publications.create(project.name, MavenPublication) { - from project.components.java - groupId project.group - artifactId project.name + if((bintrayConfig != null) && (bintrayConfig.publications) && (bintrayConfig.publications.findAll { it == project.name }.size() > 0)) { + applyPublishing(bintrayConfig) + } + } - artifact project.tasks.sourceJar - artifact project.tasks.javadocJar + void applyPublishing(BintrayConfigExtension bintrayConfig) { + project.afterEvaluate { + project.logger.info("Configuring bintray for ${project.name}") + project.configure(project) { + apply plugin: 'maven-publish' + apply plugin: 'com.jfrog.bintray' + } + def bintray = project.extensions.findByName("bintray") + configureBintray(bintray, bintrayConfig) + project.publishing.publications.create(project.name, MavenPublication) { + from project.components.java + groupId project.group + artifactId project.name - extendPomForMavenCentral(pom, bintrayConfig) - } + artifact project.tasks.sourceJar + artifact project.tasks.javadocJar + + extendPomForMavenCentral(pom, bintrayConfig) } } } diff --git a/node/build.gradle b/node/build.gradle index 2d962ed1ab..34d18c18f3 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -5,6 +5,8 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +description 'Corda node modules' + repositories { mavenLocal() mavenCentral() @@ -159,64 +161,4 @@ dependencies { task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath -} - -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['node'] - dryRun = false - pkg { - repo = 'corda' - name = 'node' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - -publishing { - publications { - node(MavenPublication) { - from components.java - artifactId 'node' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'node' - description 'Corda node modules' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } } \ No newline at end of file diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index 22a9b625d7..4f2b0d2e3f 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -7,6 +7,9 @@ apply plugin: 'us.kirchmeier.capsule' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +//name 'corda' +description 'Corda standalone node' + repositories { mavenLocal() mavenCentral() @@ -72,66 +75,4 @@ task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: project.jar) artifacts { runtimeArtifacts buildCordaJAR -} - -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['corda'] - dryRun = false - pkg { - repo = 'corda' - name = 'corda' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - -publishing { - publications { - corda(MavenPublication) { - artifactId 'corda' - - artifact sourceJar - artifact javadocJar - artifact buildCordaJAR { - classifier "" - } - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'corda' - description 'Corda standalone node' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } } \ No newline at end of file diff --git a/test-utils/build.gradle b/test-utils/build.gradle index 12515b63e5..7c8e36c2a3 100644 --- a/test-utils/build.gradle +++ b/test-utils/build.gradle @@ -4,6 +4,8 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +description 'Testing utilities for Corda' + repositories { mavenLocal() mavenCentral() @@ -49,64 +51,4 @@ dependencies { // OkHTTP: Simple HTTP library. compile 'com.squareup.okhttp3:okhttp:3.3.1' -} - -bintray { - user = System.getenv('CORDA_BINTRAY_USER') - key = System.getenv('CORDA_BINTRAY_KEY') - publications = ['testutils'] - dryRun = false - pkg { - repo = 'corda' - name = 'test-utils' - userOrg = 'r3' - licenses = ['Apache-2.0'] - - version { - gpg { - sign = true - passphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - } - } - } -} - -publishing { - publications { - testutils(MavenPublication) { - from components.java - artifactId 'test-utils' - - artifact sourceJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - name 'test-utils' - description 'Testing utilities for Corda' - url 'https://github.com/corda/corda' - scm { - url 'https://github.com/corda/corda' - } - - licenses { - license { - name 'Apache-2.0' - url 'https://www.apache.org/licenses/LICENSE-2.0' - distribution 'repo' - } - } - - developers { - developer { - id 'R3' - name 'R3' - email 'dev@corda.net' - } - } - } - } - } - } -} +} \ No newline at end of file From b24c628e348512ffab442d29f4c9383bada99276 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 11:28:22 +0000 Subject: [PATCH 16/37] Publications now publish JARs again. Install task now publishes again. --- build.gradle | 4 -- client/build.gradle | 2 - core/build.gradle | 2 - finance/build.gradle | 2 - .../net/corda/plugins/PublishTasks.groovy | 46 ++++++++++--------- node/build.gradle | 2 - test-utils/build.gradle | 3 -- 7 files changed, 25 insertions(+), 36 deletions(-) diff --git a/build.gradle b/build.gradle index 81fe22c346..9b345df920 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,6 @@ plugins { apply plugin: 'kotlin' apply plugin: 'project-report' apply plugin: 'com.github.ben-manes.versions' -apply plugin: 'maven-publish' apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordformation' @@ -153,9 +152,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) { } } -// Aliasing the publishToMavenLocal for simplicity. -task(install, dependsOn: 'publishToMavenLocal') - bintrayConfig { user = System.getenv('CORDA_BINTRAY_USER') key = System.getenv('CORDA_BINTRAY_KEY') diff --git a/client/build.gradle b/client/build.gradle index 4d2780e9bf..1748bef18d 100644 --- a/client/build.gradle +++ b/client/build.gradle @@ -1,8 +1,6 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' description 'Corda client modules' diff --git a/core/build.gradle b/core/build.gradle index 67bf903409..2265ea4402 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,8 +1,6 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' description 'Corda core' diff --git a/finance/build.gradle b/finance/build.gradle index 699f18a92e..656e4e0b1a 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -2,8 +2,6 @@ apply plugin: 'kotlin' apply plugin: CanonicalizerPlugin apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.quasar-utils' -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' description 'Corda finance modules' diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index ff59810e44..46bf6d0dc4 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -24,38 +24,40 @@ class PublishTasks implements Plugin { createTasks() createExtensions() - checkAndApplyPublishing() + checkAndConfigurePublishing() } - void checkAndApplyPublishing() { + void checkAndConfigurePublishing() { + project.logger.info("Checking whether to publish ${project.name}") def bintrayConfig = project.rootProject.extensions.findByType(BintrayConfigExtension.class) if((bintrayConfig != null) && (bintrayConfig.publications) && (bintrayConfig.publications.findAll { it == project.name }.size() > 0)) { - applyPublishing(bintrayConfig) + configurePublishing(bintrayConfig) } } - void applyPublishing(BintrayConfigExtension bintrayConfig) { + void configurePublishing(BintrayConfigExtension bintrayConfig) { project.afterEvaluate { project.logger.info("Configuring bintray for ${project.name}") - project.configure(project) { - apply plugin: 'maven-publish' - apply plugin: 'com.jfrog.bintray' - } - def bintray = project.extensions.findByName("bintray") - configureBintray(bintray, bintrayConfig) - project.publishing.publications.create(project.name, MavenPublication) { - from project.components.java - groupId project.group - artifactId project.name - - artifact project.tasks.sourceJar - artifact project.tasks.javadocJar - - extendPomForMavenCentral(pom, bintrayConfig) - } + configureMavenPublish(bintrayConfig) + configureBintray(bintrayConfig) } } + void configureMavenPublish(BintrayConfigExtension bintrayConfig) { + project.apply([plugin: 'maven-publish']) + project.publishing.publications.create(project.name, MavenPublication) { + from project.components.java + groupId project.group + artifactId project.name + + artifact project.tasks.sourceJar + artifact project.tasks.javadocJar + + extendPomForMavenCentral(pom, bintrayConfig) + } + project.task("install", dependsOn: "publishToMavenLocal") + } + // Maven central requires all of the below fields for this to be a valid POM void extendPomForMavenCentral(MavenPom pom, BintrayConfigExtension config) { pom.withXml { @@ -87,7 +89,9 @@ class PublishTasks implements Plugin { } } - void configureBintray(def bintray, BintrayConfigExtension bintrayConfig) { + void configureBintray(BintrayConfigExtension bintrayConfig) { + project.apply([plugin: 'com.jfrog.bintray']) + def bintray = project.extensions.findByName("bintray") bintray.user = bintrayConfig.user bintray.key = bintrayConfig.key bintray.publications = [ project.name ] diff --git a/node/build.gradle b/node/build.gradle index 34d18c18f3..7e39c1b5e6 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -2,8 +2,6 @@ apply plugin: 'kotlin' apply plugin: 'java' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' description 'Corda node modules' diff --git a/test-utils/build.gradle b/test-utils/build.gradle index 7c8e36c2a3..b220b3ce4b 100644 --- a/test-utils/build.gradle +++ b/test-utils/build.gradle @@ -1,8 +1,6 @@ apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' description 'Testing utilities for Corda' @@ -20,7 +18,6 @@ repositories { //noinspection GroovyAssignabilityCheck configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment runtime.exclude module: 'isolated' } From 1463cd2b3780c228c544fcc34bb5636c7b6428d1 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 13:34:04 +0000 Subject: [PATCH 17/37] Renamec capsule module to corda. --- build.gradle | 4 ++-- docs/source/example-code/build.gradle | 4 ++-- gradle-plugins/build.gradle | 11 +---------- gradle-plugins/publish-utils/build.gradle | 3 +++ .../main/groovy/net/corda/plugins/PublishTasks.groovy | 10 ++++++++++ node/{capsule => corda}/build.gradle | 9 +++++---- samples/attachment-demo/build.gradle | 2 +- samples/irs-demo/build.gradle | 2 +- samples/network-visualiser/build.gradle | 2 +- samples/raft-notary-demo/build.gradle | 2 +- samples/simm-valuation-demo/build.gradle | 2 +- samples/trader-demo/build.gradle | 2 +- settings.gradle | 2 +- 13 files changed, 30 insertions(+), 25 deletions(-) rename node/{capsule => corda}/build.gradle (94%) diff --git a/build.gradle b/build.gradle index 9b345df920..e3f176c856 100644 --- a/build.gradle +++ b/build.gradle @@ -92,7 +92,7 @@ repositories { dependencies { compile project(':node') compile "com.google.guava:guava:19.0" - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') } task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { @@ -162,7 +162,7 @@ bintrayConfig { projectUrl = 'https://github.com/corda/corda' gpgSign = true gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - publications = ['client', 'core', 'finance', 'node', 'test-utils'] + publications = ['client', 'core', 'corda', 'finance', 'node', 'test-utils'] license { name = 'Apache-2.0' url = 'https://www.apache.org/licenses/LICENSE-2.0' diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle index 9bc0aeb012..08ad1593e4 100644 --- a/docs/source/example-code/build.gradle +++ b/docs/source/example-code/build.gradle @@ -36,7 +36,7 @@ sourceSets { } } -compileTestJava.dependsOn tasks.getByPath(':node:capsule:buildCordaJAR') +compileTestJava.dependsOn tasks.getByPath(':node:corda:buildCordaJAR') dependencies { compile project(':core') @@ -48,7 +48,7 @@ dependencies { exclude group: "bouncycastle" } - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') } mainClassName = "net.corda.docs.ClientRpcTutorialKt" diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index 7a15cb8840..7c7a233f76 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -16,7 +16,6 @@ buildscript { } } -apply plugin: 'maven-publish' apply plugin: 'net.corda.plugins.publish-utils' allprojects { @@ -24,10 +23,6 @@ allprojects { group 'net.corda.plugins' } -subprojects { - task(install, dependsOn: 'publishToMavenLocal') -} - bintrayConfig { user = System.getenv('CORDA_BINTRAY_USER') key = System.getenv('CORDA_BINTRAY_KEY') @@ -49,8 +44,4 @@ bintrayConfig { name = 'R3' email = 'dev@corda.net' } -} - -// Aliasing the publishToMavenLocal for simplicity. -task(install, dependsOn: 'publishToMavenLocal') - +} \ No newline at end of file diff --git a/gradle-plugins/publish-utils/build.gradle b/gradle-plugins/publish-utils/build.gradle index bdaff87a9e..430696ee1a 100644 --- a/gradle-plugins/publish-utils/build.gradle +++ b/gradle-plugins/publish-utils/build.gradle @@ -81,3 +81,6 @@ publishing { } } } + +// Aliasing the publishToMavenLocal for simplicity. +task(install, dependsOn: 'publishToMavenLocal') diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index 46bf6d0dc4..5eb9637c2f 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -24,6 +24,7 @@ class PublishTasks implements Plugin { createTasks() createExtensions() + createConfigurations() checkAndConfigurePublishing() } @@ -53,6 +54,11 @@ class PublishTasks implements Plugin { artifact project.tasks.sourceJar artifact project.tasks.javadocJar + project.configurations.publish.artifacts.each { + println("Adding artifact: $it") + delegate.artifact it + } + extendPomForMavenCentral(pom, bintrayConfig) } project.task("install", dependsOn: "publishToMavenLocal") @@ -125,4 +131,8 @@ class PublishTasks implements Plugin { project.extensions.create("bintrayConfig", BintrayConfigExtension) } } + + void createConfigurations() { + project.configurations.create("publish") + } } diff --git a/node/capsule/build.gradle b/node/corda/build.gradle similarity index 94% rename from node/capsule/build.gradle rename to node/corda/build.gradle index 4f2b0d2e3f..fd61034097 100644 --- a/node/capsule/build.gradle +++ b/node/corda/build.gradle @@ -5,9 +5,7 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'us.kirchmeier.capsule' apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' -//name 'corda' description 'Corda standalone node' repositories { @@ -37,12 +35,12 @@ targetCompatibility = 1.6 sourceSets { test { resources { - srcDir "../config/test" + srcDir "../../config/test" } } main { resources { - srcDir "../config/dev" + srcDir "../../config/dev" } } } @@ -75,4 +73,7 @@ task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: project.jar) artifacts { runtimeArtifacts buildCordaJAR + publish buildCordaJAR { + classifier "" + } } \ No newline at end of file diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index 89991bc556..dccad3dce0 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 0400939e62..67d273888e 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -48,7 +48,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/network-visualiser/build.gradle b/samples/network-visualiser/build.gradle index 5a0169a4ec..ce2184b414 100644 --- a/samples/network-visualiser/build.gradle +++ b/samples/network-visualiser/build.gradle @@ -21,7 +21,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/raft-notary-demo/build.gradle b/samples/raft-notary-demo/build.gradle index c0ed340679..5707e7a24e 100644 --- a/samples/raft-notary-demo/build.gradle +++ b/samples/raft-notary-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index d444858e82..53c5f1ea8d 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -41,7 +41,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 403f5b879b..aac811f0f6 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') + runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/settings.gradle b/settings.gradle index b63878100e..34a1a51567 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ include 'finance' include 'finance:isolated' include 'core' include 'node' -include 'node:capsule' +include 'node:corda' include 'client' include 'experimental' include 'experimental:sandbox' From a0d5426ced1716030ec4c3e933dc3d7866c8bd64 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 15:03:33 +0000 Subject: [PATCH 18/37] Due to insurmountable issues with publishing the JARs when the project is called Corda it has been renamed back to capsule. --- build.gradle | 2 +- docs/source/example-code/build.gradle | 4 +- node/corda/build.gradle | 79 ------------------------ samples/attachment-demo/build.gradle | 2 +- samples/irs-demo/build.gradle | 2 +- samples/network-visualiser/build.gradle | 2 +- samples/raft-notary-demo/build.gradle | 2 +- samples/simm-valuation-demo/build.gradle | 2 +- samples/trader-demo/build.gradle | 2 +- 9 files changed, 9 insertions(+), 88 deletions(-) delete mode 100644 node/corda/build.gradle diff --git a/build.gradle b/build.gradle index e3f176c856..8cbf608b52 100644 --- a/build.gradle +++ b/build.gradle @@ -92,7 +92,7 @@ repositories { dependencies { compile project(':node') compile "com.google.guava:guava:19.0" - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') } task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle index 08ad1593e4..9bc0aeb012 100644 --- a/docs/source/example-code/build.gradle +++ b/docs/source/example-code/build.gradle @@ -36,7 +36,7 @@ sourceSets { } } -compileTestJava.dependsOn tasks.getByPath(':node:corda:buildCordaJAR') +compileTestJava.dependsOn tasks.getByPath(':node:capsule:buildCordaJAR') dependencies { compile project(':core') @@ -48,7 +48,7 @@ dependencies { exclude group: "bouncycastle" } - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') } mainClassName = "net.corda.docs.ClientRpcTutorialKt" diff --git a/node/corda/build.gradle b/node/corda/build.gradle deleted file mode 100644 index fd61034097..0000000000 --- a/node/corda/build.gradle +++ /dev/null @@ -1,79 +0,0 @@ -/** - * This build.gradle exists to publish our capsule (executable fat jar) to maven. It cannot be placed in the - * node project because the bintray plugin cannot publish two modules from one project. - */ -apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'us.kirchmeier.capsule' -apply plugin: 'maven-publish' - -description 'Corda standalone node' - -repositories { - mavenLocal() - mavenCentral() - maven { - url 'http://oss.sonatype.org/content/repositories/snapshots' - } - jcenter() - maven { - url 'https://dl.bintray.com/kotlin/exposed' - } -} - -configurations { - runtimeArtifacts.extendsFrom runtime -} - -// Force the Caplet to target Java 6. This ensures that running 'java -jar corda.jar' on any Java 6 VM upwards -// will get as far as the Capsule version checks, meaning that if your JVM is too old, you will at least get -// a sensible error message telling you what to do rather than a bytecode version exception that doesn't. -// If we introduce .java files into this module that need Java 8+ then we will have to push the caplet into -// its own module so its target can be controlled individually, but for now this suffices. -sourceCompatibility = 1.6 -targetCompatibility = 1.6 - -sourceSets { - test { - resources { - srcDir "../../config/test" - } - } - main { - resources { - srcDir "../../config/dev" - } - } -} - -dependencies { - compile project(':node') -} - -task buildCordaJAR(type: FatCapsule, dependsOn: ['jar', 'buildCertSigningRequestUtilityJAR']) { - applicationClass 'net.corda.node.MainKt' - archiveName "corda-${corda_version}.jar" - applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class', 'config/dev/log4j2.xml') - - capsuleManifest { - appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] - javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] - minJavaVersion = '1.8.0' - caplets = ['CordaCaplet'] - } -} - -task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: project.jar) { - applicationClass 'net.corda.node.utilities.certsigning.CertificateSignerKt' - archiveName 'certSigningRequestUtility.jar' - capsuleManifest { - systemProperties['log4j.configuration'] = 'log4j2.xml' - minJavaVersion = '1.8.0' - } -} - -artifacts { - runtimeArtifacts buildCordaJAR - publish buildCordaJAR { - classifier "" - } -} \ No newline at end of file diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index dccad3dce0..89991bc556 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 67d273888e..0400939e62 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -48,7 +48,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/network-visualiser/build.gradle b/samples/network-visualiser/build.gradle index ce2184b414..5a0169a4ec 100644 --- a/samples/network-visualiser/build.gradle +++ b/samples/network-visualiser/build.gradle @@ -21,7 +21,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/raft-notary-demo/build.gradle b/samples/raft-notary-demo/build.gradle index 5707e7a24e..c0ed340679 100644 --- a/samples/raft-notary-demo/build.gradle +++ b/samples/raft-notary-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 53c5f1ea8d..d444858e82 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -41,7 +41,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index aac811f0f6..403f5b879b 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node:corda", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') From 41b0b6c0b1bfb7faf0318886b4473a0b20b9ff66 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 15:25:38 +0000 Subject: [PATCH 19/37] Re-added the capsule file. Fixed deploy with another publish option. --- .../plugins/ProjectPublishExtension.groovy | 12 +++ .../net/corda/plugins/PublishTasks.groovy | 46 ++++++---- node/capsule/build.gradle | 83 +++++++++++++++++++ settings.gradle | 2 +- 4 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy create mode 100644 node/capsule/build.gradle diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy new file mode 100644 index 0000000000..fbccf8b4dd --- /dev/null +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/ProjectPublishExtension.groovy @@ -0,0 +1,12 @@ +package net.corda.plugins + +class ProjectPublishExtension { + /** + * Use a different name from the current project name for publishing + */ + String name + /** + * True when we do not want to publish default Java components + */ + Boolean disableDefaultJar = false +} \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index 5eb9637c2f..c4fe145cd1 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -18,6 +18,8 @@ import net.corda.plugins.bintray.* */ class PublishTasks implements Plugin { Project project + String publishName + ProjectPublishExtension publishConfig void apply(Project project) { this.project = project @@ -25,37 +27,50 @@ class PublishTasks implements Plugin { createTasks() createExtensions() createConfigurations() - checkAndConfigurePublishing() + + project.afterEvaluate { + configurePublishingName() + checkAndConfigurePublishing() + } + } + + void configurePublishingName() { + if(publishConfig.name != null) { + project.logger.info("Changing publishing name for ${project.name} to ${publishConfig.name}") + publishName = publishConfig.name + } else { + publishName = project.name + } } void checkAndConfigurePublishing() { - project.logger.info("Checking whether to publish ${project.name}") + project.logger.info("Checking whether to publish $publishName") def bintrayConfig = project.rootProject.extensions.findByType(BintrayConfigExtension.class) - if((bintrayConfig != null) && (bintrayConfig.publications) && (bintrayConfig.publications.findAll { it == project.name }.size() > 0)) { + if((bintrayConfig != null) && (bintrayConfig.publications) && (bintrayConfig.publications.findAll { it == publishName }.size() > 0)) { configurePublishing(bintrayConfig) } } void configurePublishing(BintrayConfigExtension bintrayConfig) { - project.afterEvaluate { - project.logger.info("Configuring bintray for ${project.name}") - configureMavenPublish(bintrayConfig) - configureBintray(bintrayConfig) - } + project.logger.info("Configuring bintray for ${publishName}") + configureMavenPublish(bintrayConfig) + configureBintray(bintrayConfig) } void configureMavenPublish(BintrayConfigExtension bintrayConfig) { project.apply([plugin: 'maven-publish']) - project.publishing.publications.create(project.name, MavenPublication) { - from project.components.java + project.publishing.publications.create(publishName, MavenPublication) { + if(!publishConfig.disableDefaultJar) { + from project.components.java + } groupId project.group - artifactId project.name + artifactId publishName artifact project.tasks.sourceJar artifact project.tasks.javadocJar project.configurations.publish.artifacts.each { - println("Adding artifact: $it") + project.logger.debug("Adding artifact: $it") delegate.artifact it } @@ -69,7 +84,7 @@ class PublishTasks implements Plugin { pom.withXml { asNode().children().last() + { resolveStrategy = Closure.DELEGATE_FIRST - name project.name + name publishName description project.description url config.projectUrl scm { @@ -100,10 +115,10 @@ class PublishTasks implements Plugin { def bintray = project.extensions.findByName("bintray") bintray.user = bintrayConfig.user bintray.key = bintrayConfig.key - bintray.publications = [ project.name ] + bintray.publications = [ publishName ] bintray.dryRun = bintrayConfig.dryRun ?: false bintray.pkg.repo = bintrayConfig.repo - bintray.pkg.name = project.name + bintray.pkg.name = publishName bintray.pkg.userOrg = bintrayConfig.org bintray.pkg.licenses = bintrayConfig.licenses bintray.pkg.version.gpg.sign = bintrayConfig.gpgSign ?: false @@ -130,6 +145,7 @@ class PublishTasks implements Plugin { if(project == project.rootProject) { project.extensions.create("bintrayConfig", BintrayConfigExtension) } + publishConfig = project.extensions.create("publish", ProjectPublishExtension) } void createConfigurations() { diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle new file mode 100644 index 0000000000..c8e64ba2e4 --- /dev/null +++ b/node/capsule/build.gradle @@ -0,0 +1,83 @@ +/** + * This build.gradle exists to publish our capsule (executable fat jar) to maven. It cannot be placed in the + * node project because the bintray plugin cannot publish two modules from one project. + */ +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'us.kirchmeier.capsule' + +description 'Corda standalone node' + +repositories { + mavenLocal() + mavenCentral() + maven { + url 'http://oss.sonatype.org/content/repositories/snapshots' + } + jcenter() + maven { + url 'https://dl.bintray.com/kotlin/exposed' + } +} + +configurations { + runtimeArtifacts.extendsFrom runtime +} + +// Force the Caplet to target Java 6. This ensures that running 'java -jar corda.jar' on any Java 6 VM upwards +// will get as far as the Capsule version checks, meaning that if your JVM is too old, you will at least get +// a sensible error message telling you what to do rather than a bytecode version exception that doesn't. +// If we introduce .java files into this module that need Java 8+ then we will have to push the caplet into +// its own module so its target can be controlled individually, but for now this suffices. +sourceCompatibility = 1.6 +targetCompatibility = 1.6 + +sourceSets { + test { + resources { + srcDir "../../config/test" + } + } + main { + resources { + srcDir "../../config/dev" + } + } +} + +dependencies { + compile project(':node') +} + +task buildCordaJAR(type: FatCapsule, dependsOn: [':node:jar', 'buildCertSigningRequestUtilityJAR']) { + applicationClass 'net.corda.node.MainKt' + archiveName "corda-${corda_version}.jar" + applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class', 'config/dev/log4j2.xml') + + capsuleManifest { + appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"] + javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] + minJavaVersion = '1.8.0' + caplets = ['CordaCaplet'] + } +} + +task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: [':node:jar']) { + applicationClass 'net.corda.node.utilities.certsigning.CertificateSignerKt' + archiveName 'certSigningRequestUtility.jar' + capsuleManifest { + systemProperties['log4j.configuration'] = 'log4j2.xml' + minJavaVersion = '1.8.0' + } +} + +artifacts { + runtimeArtifacts buildCordaJAR + publish buildCordaJAR { + classifier "" + } +} + +publish { + name = 'corda' + disableDefaultJar = true +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 34a1a51567..b63878100e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ include 'finance' include 'finance:isolated' include 'core' include 'node' -include 'node:corda' +include 'node:capsule' include 'client' include 'experimental' include 'experimental:sandbox' From a6d1a0767098dc8d7e23d09e018e5a9fc2dec01e Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 15:55:20 +0000 Subject: [PATCH 20/37] Added documentation for the new publishing functionality of publish-utils. --- gradle-plugins/publish-utils/README.rst | 67 +++++++++++++++++++ .../corda/plugins/bintray/Developer.groovy | 9 +++ .../net/corda/plugins/bintray/License.groovy | 9 +++ 3 files changed, 85 insertions(+) diff --git a/gradle-plugins/publish-utils/README.rst b/gradle-plugins/publish-utils/README.rst index e75667abc9..3399688602 100644 --- a/gradle-plugins/publish-utils/README.rst +++ b/gradle-plugins/publish-utils/README.rst @@ -23,3 +23,70 @@ It is used within the `publishing` block of a build.gradle as such; } } +Bintray Publishing +------------------ + +For large multibuild projects it can be inconvenient to store the entire configuration for bintray and maven central +per project (with a bintray and publishing block with extended POM information). Publish utils can bring the number of +configuration blocks down to one in the ideal scenario. + +To use this plugin you must first apply it to both the root project and any project that will be published with + +.. code-block:: text + + apply plugin: 'net.corda.plugins.publish-utils' + +Next you must setup the general bintray configuration you wish to use project wide, for example: + +.. code-block:: text + + bintrayConfig { + user = + key = + repo = 'example repo' + org = 'example organisation' + licenses = ['a license'] + vcsUrl = 'https://example.com' + projectUrl = 'https://example.com' + gpgSign = true // Whether to GPG sign + gpgPassphrase = // Only required if gpgSign is true and your key is passworded + publications = ['example'] // a list of publications (see below) + license { + name = 'example' + url = 'https://example.com' + distribution = 'repo' + } + developer { + id = 'a developer id' + name = 'a developer name' + email = 'example@example.com' + } + } + +.. note:: You can currently only have one license and developer in the maven POM sections + +**Publications** + +This plugin assumes, by default, that publications match the name of the project. This means, by default, you can +just list the names of the projects you wish to publish (eg; to publish `test:myapp` you need `publications = ['myapp']`. +If a project requires a different name you can configure it *per project* with the project configuration block. + +The project configuration block has the following structure; + +.. code-block:: text + + publish { + name = 'non-default-project-name' + disableDefaultJar = false // set to true to disable the default JAR being created (eg; when creating a fat JAR) + } + +**Artifacts** + +To add additional artifacts to the project you can use the default gradle `artifacts` block with the `publish` +configuration. For example: + + artifacts { + publish buildFatJar { + // You can configure this as a regular maven publication + } + } diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy index 1f8df63ece..1d66f68c7d 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/Developer.groovy @@ -1,7 +1,16 @@ package net.corda.plugins.bintray class Developer { + /** + * A unique identifier the developer (eg; organisation ID) + */ String id + /** + * The full name of the developer + */ String name + /** + * An email address for contacting the developer + */ String email } \ No newline at end of file diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy index ca03d4ade4..1d06867bcf 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/bintray/License.groovy @@ -1,7 +1,16 @@ package net.corda.plugins.bintray class License { + /** + * The name of license (eg; Apache 2.0) + */ String name + /** + * URL to the full license file + */ String url + /** + * The distribution level this license corresponds to (eg: repo) + */ String distribution } \ No newline at end of file From 1b052b07ec83c281b61d37a585c52b858bace4dd Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 16:03:30 +0000 Subject: [PATCH 21/37] Returned to using more readable bintray syntax. --- .../net/corda/plugins/PublishTasks.groovy | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy index c4fe145cd1..d94880dfe5 100644 --- a/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy +++ b/gradle-plugins/publish-utils/src/main/groovy/net/corda/plugins/PublishTasks.groovy @@ -112,17 +112,25 @@ class PublishTasks implements Plugin { void configureBintray(BintrayConfigExtension bintrayConfig) { project.apply([plugin: 'com.jfrog.bintray']) - def bintray = project.extensions.findByName("bintray") - bintray.user = bintrayConfig.user - bintray.key = bintrayConfig.key - bintray.publications = [ publishName ] - bintray.dryRun = bintrayConfig.dryRun ?: false - bintray.pkg.repo = bintrayConfig.repo - bintray.pkg.name = publishName - bintray.pkg.userOrg = bintrayConfig.org - bintray.pkg.licenses = bintrayConfig.licenses - bintray.pkg.version.gpg.sign = bintrayConfig.gpgSign ?: false - bintray.pkg.version.gpg.passphrase = bintrayConfig.gpgPassphrase + project.bintray { + user = bintrayConfig.user + key = bintrayConfig.key + publications = [ publishName ] + dryRun = bintrayConfig.dryRun ?: false + pkg { + repo = bintrayConfig.repo + name = publishName + userOrg = bintrayConfig.org + licenses = bintrayConfig.licenses + + version { + gpg { + sign = bintrayConfig.gpgSign ?: false + passphrase = bintrayConfig.gpgPassphrase + } + } + } + } } void createTasks() { From 87e5c9bf46c3552d52b2ddec0ace1408e0903f2f Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 16:14:06 +0000 Subject: [PATCH 22/37] Added documentation and buildscript to allow full bootstrapping without modifying build files. --- gradle-plugins/README.rst | 7 ++++++- gradle-plugins/publish-utils/build.gradle | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/gradle-plugins/README.rst b/gradle-plugins/README.rst index 4ce50a24c2..647398b752 100644 --- a/gradle-plugins/README.rst +++ b/gradle-plugins/README.rst @@ -15,5 +15,10 @@ Installing If you need to bootstrap the corda repository you can install these plugins with +.. code-block:: text + + cd publish-utils + ../../gradle install + cd ../ + ../gradle install - gradle install \ No newline at end of file diff --git a/gradle-plugins/publish-utils/build.gradle b/gradle-plugins/publish-utils/build.gradle index 430696ee1a..b8733fed95 100644 --- a/gradle-plugins/publish-utils/build.gradle +++ b/gradle-plugins/publish-utils/build.gradle @@ -2,6 +2,21 @@ apply plugin: 'groovy' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' +// Used for bootstrapping project +buildscript { + ext.gradle_plugins_version = "0.6.2" // Our version: bump this on release. + + repositories { + jcenter() + } + + dependencies { + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' + } +} + +version "$gradle_plugins_version" + dependencies { compile gradleApi() compile localGroovy() From f1daae049135ea9c771d2efebd187d40e4ca593c Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 6 Dec 2016 16:29:15 +0000 Subject: [PATCH 23/37] To avoid needing to bump three different files upon updates, a common publish properties file has been added. --- build.gradle | 6 +++++- gradle-plugins/build.gradle | 8 ++++++-- gradle-plugins/publish-utils/build.gradle | 6 +++++- publish.properties | 1 + 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 publish.properties diff --git a/build.gradle b/build.gradle index 8cbf608b52..f49282bb87 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,11 @@ buildscript { + // For sharing constants between builds + Properties props = new Properties() + file("publish.properties").withInputStream { props.load(it) } + // Our version: bump this on release. ext.corda_version = "0.7-SNAPSHOT" - ext.gradle_plugins_version = "0.6.2" + ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion") ext.kotlin_version = '1.0.5' ext.quasar_version = '0.7.6' ext.asm_version = '0.5.3' diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index 7c7a233f76..7fd9b55d4e 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -2,8 +2,12 @@ // or if you are developing these plugins. See the readme for more information. buildscript { - ext.gradle_plugins_version = "0.6.2" // Our version: bump this on release. - ext.corda_published_version = "0.6.2" // Depend on our existing published publishing plugin. + // For sharing constants between builds + Properties props = new Properties() + file("../publish.properties").withInputStream { props.load(it) } + + ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion") // Our version: bump this on release. + ext.corda_published_version = "0.6.2" // Only needs to be bumped when the publishing plugin is changed. repositories { mavenLocal() diff --git a/gradle-plugins/publish-utils/build.gradle b/gradle-plugins/publish-utils/build.gradle index b8733fed95..733de1bbee 100644 --- a/gradle-plugins/publish-utils/build.gradle +++ b/gradle-plugins/publish-utils/build.gradle @@ -4,7 +4,11 @@ apply plugin: 'com.jfrog.bintray' // Used for bootstrapping project buildscript { - ext.gradle_plugins_version = "0.6.2" // Our version: bump this on release. + // For sharing constants between builds + Properties props = new Properties() + file("../../publish.properties").withInputStream { props.load(it) } + + ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion") repositories { jcenter() diff --git a/publish.properties b/publish.properties new file mode 100644 index 0000000000..1c38857662 --- /dev/null +++ b/publish.properties @@ -0,0 +1 @@ +gradlePluginsVersion=0.6.2 \ No newline at end of file From f5e03099e3ff8d540b757857327dcfaf3a35be88 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 7 Dec 2016 16:43:05 +0000 Subject: [PATCH 24/37] Updated to newer Kotlin version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f49282bb87..b619f77eee 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { // Our version: bump this on release. ext.corda_version = "0.7-SNAPSHOT" ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion") - ext.kotlin_version = '1.0.5' + ext.kotlin_version = '1.0.5-2' ext.quasar_version = '0.7.6' ext.asm_version = '0.5.3' ext.artemis_version = '1.4.0' From e4187c59e27214b9af74fda415a1c0e4bbf2beba Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 8 Dec 2016 12:13:05 +0000 Subject: [PATCH 25/37] Added documentation on how to publish Corda. --- docs/source/publishing-corda.rst | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/source/publishing-corda.rst diff --git a/docs/source/publishing-corda.rst b/docs/source/publishing-corda.rst new file mode 100644 index 0000000000..2c07f1b598 --- /dev/null +++ b/docs/source/publishing-corda.rst @@ -0,0 +1,76 @@ +Publishing Corda +================ + +Before Publishing +----------------- + +Before publishing you must make sure the version you plan to publish has a unique version number. Jcenter and Maven +Central will not allow overwriting old versions _unless_ the version is a snapshot. + +This guide assumes you are trying to publish to net.corda.*. Any other Maven coordinates require approval from Jcenter +and Maven Central. + +Publishing Locally +------------------ + +To publish the codebase locally to Maven Local you must run: + +.. code-block:: text + + gradlew install + +.. note:: This command is an alias for `publishToMavenLocal`. + +Publishing to Jcenter +--------------------- + +.. note:: The module you wish to publish must be linked to jcenter in bintray. Only the founding account can do this. + +To publish to Jcenter you must first have the following; + +1. An account on bintray in the R3 organisation +2. Our GPG key's passphrase for signing the binaries to publish + +Getting Setup +````````````` + +You must now set the following environment variables: + +* CORDA_BINTRAY_USER your Bintray username +* CORDA_BINTRAY_KEY to your bintray API key (found at: https://bintray.com/profile/edit) +* CORDA_BINTRAY_GPG_PASSPHRASE to our GPG passphrase + +Publishing +`````````` + +Once you are setup you can upload all modules in a project with + +.. code-block:: text + + gradlew bintrayUpload + +Now login to Bintray and navigate to the corda repository, you will see a box stating you have published N files +and asking if you wish to publish. You can now publish to Bintray and Jcenter by clicking this button. + +.. warning:: Before publishing you should check that all of the files are uploaded and are signed. + +Within a minute your new version will be available to download and use. + +Publishing to Maven Central +--------------------------- + +To publish to Maven Central you need the following; + +1. An admin account on our Bintray R3 organisation +2. A published version in Bintray +3. An account with our Sonatype organisation (Maven Central's host) + +Publishing +`````````` + +1. Publish to Bintray +2. Navigate to the project you wish to publist +3. Click "Maven Central" +4. Enter your Sonatype credentials to publish a new version + +.. note:: The project you publish must be already published to Bintray and the project must be linked to Jcenter \ No newline at end of file From 467e2fc4aaa6a6120a2f21d0400e78fc18387c43 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 8 Dec 2016 12:14:17 +0000 Subject: [PATCH 26/37] Added new documentation to the index. --- docs/source/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/index.rst b/docs/source/index.rst index 00373ace5a..a67d3f46e7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -107,6 +107,7 @@ Read on to learn: release-notes codestyle building-the-docs + publishing-corda .. toctree:: :maxdepth: 2 From 5b4babf3297bf404a7aafb4f4821d3555ab614bd Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 8 Dec 2016 12:18:26 +0000 Subject: [PATCH 27/37] Removed redundant gradle extension variable to reduce complexity of build.gradle and clear up the process of changing the version numbers. --- gradle-plugins/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index 7fd9b55d4e..f961d7f4e0 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -6,8 +6,8 @@ buildscript { Properties props = new Properties() file("../publish.properties").withInputStream { props.load(it) } - ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion") // Our version: bump this on release. - ext.corda_published_version = "0.6.2" // Only needs to be bumped when the publishing plugin is changed. + // If you bump this version you must re-bootstrap the codebase. See the README for more information. + ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion") repositories { mavenLocal() @@ -15,7 +15,7 @@ buildscript { } dependencies { - classpath "net.corda.plugins:publish-utils:$corda_published_version" + classpath "net.corda.plugins:publish-utils:$gradle_plugins_version" classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' } } From c02077bdbc59892afffbb92ab952ebaa7d971d1c Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 8 Dec 2016 13:44:50 +0000 Subject: [PATCH 28/37] Updates the cordapp-template docs to reflect new structure. --- docs/source/tutorial-cordapp.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index 2097ac4267..a65f91ca7a 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -252,7 +252,7 @@ example CorDapp. The ``deployNodes`` Gradle task allows you easily create a formation of Corda nodes. In the case of the example CorDapp we are creating ``four`` nodes. -After the building process has finished to see the newly built nodes, you can navigate to the ``/build/nodes`` folder +After the building process has finished to see the newly built nodes, you can navigate to the ``kotlin/build/nodes`` folder located in the ``cordapp-template`` root directory. You can ignore the other folders in ``/build`` for now. The ``nodes`` folder has the following structure: @@ -300,7 +300,7 @@ Running the Sample CorDapp Running the Sample CorDapp from the command line ------------------------------------------------ -To run the sample CorDapp navigate to the ``build/nodes`` folder and execute the ``runnodes`` shell script with: +To run the sample CorDapp navigate to the ``kotlin/build/nodes`` folder and execute the ``runnodes`` shell script with: Unix: ``./runnodes`` or ``sh runnodes`` @@ -319,7 +319,7 @@ message and some pertinent config information, see below: --- DEVELOPER SNAPSHOT ------------------------------------------------------------ - Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-template/build/nodes/nodea/logs + Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-template/kotlin/build/nodes/nodea/logs Database connection url is : jdbc:h2:tcp://10.18.0.196:50661/node Node listening on address : localhost:10004 Loaded plugins : com.example.plugin.ExamplePlugin @@ -368,7 +368,7 @@ Running CorDapps on separate machines Corda nodes can be run on separate machines with little additional configuration to the above instructions. When you have successfully run the ``deployNodes`` gradle task, choose which nodes you would like to run on separate -machines. Copy the folders for those nodes from ``build/nodes`` to the other machines. Make sure that you set the +machines. Copy the folders for those nodes from ``kotlin/build/nodes`` to the other machines. Make sure that you set the ``networkMapAddress`` property in ``node.conf`` to the correct hostname:port where the network map service node is hosted. @@ -434,7 +434,7 @@ The CorDapp defines a few HTTP API end-points and also serves some static web co list purchase orders and add purchase orders. The nodes can be found using the following port numbers, defined in build.gradle and the respective node.conf file for -each node found in `build/nodes/NodeX`` etc: +each node found in `kotlin/build/nodes/NodeX`` etc: * Controller: ``localhost:10003`` * NodeA: ``localhost:10005`` @@ -758,7 +758,7 @@ like to deploy for testing. See further details below: .. sourcecode:: groovy task deployNodes(type: com.r3corda.plugins.Cordform, dependsOn: ['build']) { - directory "./build/nodes" // The output directory. + directory "./kotlin/build/nodes" // The output directory. networkMap "Controller" // The artemis address of the node to be used as the network map. node { name "Controller" // Artemis name of node to be deployed. @@ -804,7 +804,7 @@ Re-Deploying Your Nodes Locally If you need to create any additional nodes you can do it via the ``build.gradle`` file as discussed above in ``the build.gradle file`` and in more detail in the "cordFormation" section. -You may also wish to edit the ``/build/nodes//node.conf`` files for your nodes. For more information on +You may also wish to edit the ``/kotlin/build/nodes//node.conf`` files for your nodes. For more information on doing this, see the :doc:`Corda configuration file ` page. Once you have made some changes to your CorDapp you can redeploy it with the following command: From 5f603a94f038af1816ca7fc5e8b5efa2f792884c Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 8 Dec 2016 14:06:32 +0000 Subject: [PATCH 29/37] Minor: add a TODO --- .../corda/node/services/messaging/ArtemisMessagingComponent.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt index 004e7db67b..26ae128a26 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt @@ -136,6 +136,8 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() { TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2", TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true + + // TODO: Set up the connector's host name verifier logic to ensure we connect to the expected node even in case of MITM or BGP hijacks ) ) } From 0378b8d785aa5f511c8c10565354d29d7374f485 Mon Sep 17 00:00:00 2001 From: "rick.parker" Date: Wed, 30 Nov 2016 17:10:29 +0000 Subject: [PATCH 30/37] Buffer observations until database commit. --- core/src/main/kotlin/net/corda/core/Utils.kt | 12 ++ .../net/corda/core/node/services/Services.kt | 13 +- .../docs/FxTransactionBuildTutorialTest.kt | 21 +-- .../WorkflowTransactionBuildTutorialTest.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 2 +- .../node/services/api/ServiceHubInternal.kt | 2 +- .../events/ScheduledActivityObserver.kt | 2 +- .../network/InMemoryNetworkMapCache.kt | 11 +- .../DBTransactionMappingStorage.kt | 2 +- .../persistence/DBTransactionStorage.kt | 2 +- .../node/services/schema/HibernateObserver.kt | 2 +- .../statemachine/StateMachineManager.kt | 8 +- .../vault/CashBalanceAsMetricsObserver.kt | 18 ++- .../node/services/vault/NodeVaultService.kt | 9 +- .../corda/node/utilities/DatabaseSupport.kt | 60 +++++++- .../node/services/ArtemisMessagingTests.kt | 2 +- .../services/InMemoryNetworkMapCacheTest.kt | 5 +- .../services/InMemoryNetworkMapServiceTest.kt | 5 +- .../corda/node/utilities/ObservablesTests.kt | 142 ++++++++++++++++++ 19 files changed, 280 insertions(+), 40 deletions(-) create mode 100644 node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt diff --git a/core/src/main/kotlin/net/corda/core/Utils.kt b/core/src/main/kotlin/net/corda/core/Utils.kt index 97d8d1dce8..fea248b305 100644 --- a/core/src/main/kotlin/net/corda/core/Utils.kt +++ b/core/src/main/kotlin/net/corda/core/Utils.kt @@ -13,6 +13,8 @@ import kotlinx.support.jdk7.use import net.corda.core.crypto.newSecureRandom import org.slf4j.Logger import rx.Observable +import rx.Observer +import rx.subjects.PublishSubject import rx.subjects.UnicastSubject import java.io.BufferedInputStream import java.io.InputStream @@ -363,5 +365,15 @@ fun Observable.bufferUntilSubscribed(): Observable { return subject.doOnUnsubscribe { subscription.unsubscribe() } } +/** + * Copy an [Observer] to multiple other [Observer]s. + */ +fun Observer.tee(vararg teeTo: Observer): Observer { + val subject = PublishSubject.create() + subject.subscribe(this) + teeTo.forEach { subject.subscribe(it) } + return subject +} + /** Allows summing big decimals that are in iterable collections */ fun Iterable.sum(): BigDecimal = fold(BigDecimal.ZERO) { a, b -> a + b } diff --git a/core/src/main/kotlin/net/corda/core/node/services/Services.kt b/core/src/main/kotlin/net/corda/core/node/services/Services.kt index ece53304f7..5f5215f1ac 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/Services.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/Services.kt @@ -100,8 +100,19 @@ interface VaultService { val currentVault: Vault /** + * Prefer the use of [updates] unless you know why you want to use this instead. + * * Get a synchronous Observable of updates. When observations are pushed to the Observer, the Vault will already incorporate - * the update. + * the update, and the database transaction associated with the update will still be open and current. If for some + * reason the processing crosses outside of the database transaction (for example, the update is pushed outside the current + * JVM or across to another [Thread] which is executing in a different database transaction) then the Vault may + * not incorporate the update due to racing with committing the current database transaction. + */ + val rawUpdates: Observable + + /** + * Get a synchronous Observable of updates. When observations are pushed to the Observer, the Vault will already incorporate + * the update, and the database transaction associated with the update will have been committed and closed. */ val updates: Observable diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index 22335d0483..4467b69b16 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -3,9 +3,7 @@ package net.corda.docs import com.google.common.util.concurrent.SettableFuture import net.corda.core.contracts.* import net.corda.core.getOrThrow -import net.corda.core.node.ServiceHub import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.linearHeadsOfType import net.corda.core.serialization.OpaqueBytes import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY_KEY @@ -18,7 +16,6 @@ import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before import org.junit.Test -import java.util.* import kotlin.test.assertEquals class FxTransactionBuildTutorialTest { @@ -69,13 +66,13 @@ class FxTransactionBuildTutorialTest { printBalances() // Setup some futures on the vaults to await the arrival of the exchanged funds at both nodes - val done2 = SettableFuture.create>>() - val done3 = SettableFuture.create>>() + val done2 = SettableFuture.create() + val done3 = SettableFuture.create() val subs2 = nodeA.services.vaultService.updates.subscribe { - done2.set(nodeA.services.vaultService.cashBalances) + done2.set(Unit) } val subs3 = nodeB.services.vaultService.updates.subscribe { - done3.set(nodeB.services.vaultService.cashBalances) + done3.set(Unit) } // Now run the actual Fx exchange val doIt = nodeA.services.startFlow(ForeignExchangeFlow("trade1", @@ -86,8 +83,14 @@ class FxTransactionBuildTutorialTest { // wait for the flow to finish and the vault updates to be done doIt.resultFuture.getOrThrow() // Get the balances when the vault updates - val balancesA = done2.get() - val balancesB = done3.get() + done2.get() + val balancesA = databaseTransaction(nodeA.database) { + nodeA.services.vaultService.cashBalances + } + done3.get() + val balancesB = databaseTransaction(nodeB.database) { + nodeB.services.vaultService.cashBalances + } subs2.unsubscribe() subs3.unsubscribe() println("BalanceA\n" + balancesA) diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index d663b1b36b..a39c6a3734 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -53,7 +53,7 @@ class WorkflowTransactionBuildTutorialTest { net.stopNodes() } - //@Test + @Test fun `Run workflow to completion`() { // Setup a vault subscriber to wait for successful upload of the proposal to NodeB val done1 = SettableFuture.create() diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index ac5bea5091..4b43c904aa 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -247,7 +247,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo // TODO: this model might change but for now it provides some de-coupling // Add vault observers - CashBalanceAsMetricsObserver(services) + CashBalanceAsMetricsObserver(services, database) ScheduledActivityObserver(services) HibernateObserver(services) diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index ac32fbf048..5ef9163882 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -42,7 +42,7 @@ abstract class ServiceHubInternal : PluginServiceHub { abstract val schemaService: SchemaService abstract override val networkService: MessagingServiceInternal - + /** * Given a list of [SignedTransaction]s, writes them to the given storage for validated transactions and then * sends them to the vault for further processing. This is intended for implementations to call from diff --git a/node/src/main/kotlin/net/corda/node/services/events/ScheduledActivityObserver.kt b/node/src/main/kotlin/net/corda/node/services/events/ScheduledActivityObserver.kt index 5a2acb09d2..5babdb4ea6 100644 --- a/node/src/main/kotlin/net/corda/node/services/events/ScheduledActivityObserver.kt +++ b/node/src/main/kotlin/net/corda/node/services/events/ScheduledActivityObserver.kt @@ -13,7 +13,7 @@ import net.corda.node.services.api.ServiceHubInternal */ class ScheduledActivityObserver(val services: ServiceHubInternal) { init { - services.vaultService.updates.subscribe { update -> + services.vaultService.rawUpdates.subscribe { update -> update.consumed.forEach { services.schedulerService.unscheduleStateActivity(it) } update.produced.forEach { scheduleStateActivity(it, services.flowLogicRefFactory) } } diff --git a/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt index 3dafa190c6..09ff3ce2c2 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt @@ -24,6 +24,7 @@ import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_ import net.corda.node.services.network.NetworkMapService.FetchMapResponse import net.corda.node.services.network.NetworkMapService.SubscribeResponse import net.corda.node.utilities.AddOrRemove +import net.corda.node.utilities.bufferUntilDatabaseCommit import rx.Observable import rx.subjects.PublishSubject import java.security.SignatureException @@ -42,7 +43,9 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach override val partyNodes: List get() = registeredNodes.map { it.value } override val networkMapNodes: List get() = getNodesWithService(NetworkMapService.type) private val _changed = PublishSubject.create() - override val changed: Observable = _changed + override val changed: Observable get() = _changed + private val changePublisher: rx.Observer get() = _changed.bufferUntilDatabaseCommit() + private val _registrationFuture = SettableFuture.create() override val mapServiceRegistered: ListenableFuture get() = _registrationFuture @@ -91,9 +94,9 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach synchronized(_changed) { val previousNode = registeredNodes.put(node.legalIdentity, node) if (previousNode == null) { - _changed.onNext(MapChange.Added(node)) + changePublisher.onNext(MapChange.Added(node)) } else if (previousNode != node) { - _changed.onNext(MapChange.Modified(node, previousNode)) + changePublisher.onNext(MapChange.Modified(node, previousNode)) } } } @@ -101,7 +104,7 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach override fun removeNode(node: NodeInfo) { synchronized(_changed) { registeredNodes.remove(node.legalIdentity) - _changed.onNext(MapChange.Removed(node)) + changePublisher.onNext(MapChange.Removed(node)) } } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt index ee4e962c94..6cca46d1b9 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt @@ -50,7 +50,7 @@ class DBTransactionMappingStorage : StateMachineRecordedTransactionMappingStorag override fun addMapping(stateMachineRunId: StateMachineRunId, transactionId: SecureHash) { mutex.locked { stateMachineTransactionMap[transactionId] = stateMachineRunId - updates.onNext(StateMachineTransactionMapping(stateMachineRunId, transactionId)) + updates.bufferUntilDatabaseCommit().onNext(StateMachineTransactionMapping(stateMachineRunId, transactionId)) } } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt index fca852a687..3ee8a0416f 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt @@ -40,7 +40,7 @@ class DBTransactionStorage : TransactionStorage { val old = txStorage.get(transaction.id) if (old == null) { txStorage.put(transaction.id, transaction) - updatesPublisher.onNext(transaction) + updatesPublisher.bufferUntilDatabaseCommit().onNext(transaction) true } else { false diff --git a/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt b/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt index f8dd0d4599..5e32108c33 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt @@ -35,7 +35,7 @@ class HibernateObserver(services: ServiceHubInternal) { val sessionFactories = ConcurrentHashMap() init { - services.vaultService.updates.subscribe { persist(it.produced) } + services.vaultService.rawUpdates.subscribe { persist(it.produced) } } private fun sessionFactoryForSchema(schema: MappedSchema): SessionFactory { diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt index 2d947cd152..260bd19d6c 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt @@ -29,6 +29,7 @@ import net.corda.node.services.api.CheckpointStorage import net.corda.node.services.api.ServiceHubInternal import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AffinityExecutor +import net.corda.node.utilities.bufferUntilDatabaseCommit import net.corda.node.utilities.isolatedTransaction import org.apache.activemq.artemis.utils.ReusableLatch import org.jetbrains.exposed.sql.Database @@ -93,7 +94,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, val changesPublisher = PublishSubject.create() fun notifyChangeObservers(psm: FlowStateMachineImpl<*>, addOrRemove: AddOrRemove) { - changesPublisher.onNext(Change(psm.logic, addOrRemove, psm.id)) + changesPublisher.bufferUntilDatabaseCommit().onNext(Change(psm.logic, addOrRemove, psm.id)) } }) @@ -393,13 +394,14 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, * restarted with checkpointed state machines in the storage service. */ fun add(logic: FlowLogic): FlowStateMachine { - val fiber = createFiber(logic) // We swap out the parent transaction context as using this frequently leads to a deadlock as we wait // on the flow completion future inside that context. The problem is that any progress checkpoints are // unable to acquire the table lock and move forward till the calling transaction finishes. // Committing in line here on a fresh context ensure we can progress. - isolatedTransaction(database) { + val fiber = isolatedTransaction(database) { + val fiber = createFiber(logic) updateCheckpoint(fiber) + fiber } // If we are not started then our checkpoint will be picked up during start mutex.locked { diff --git a/node/src/main/kotlin/net/corda/node/services/vault/CashBalanceAsMetricsObserver.kt b/node/src/main/kotlin/net/corda/node/services/vault/CashBalanceAsMetricsObserver.kt index a9fe08bf33..b6e05fdf17 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/CashBalanceAsMetricsObserver.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/CashBalanceAsMetricsObserver.kt @@ -3,12 +3,14 @@ package net.corda.node.services.vault import com.codahale.metrics.Gauge import net.corda.core.node.services.VaultService import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.utilities.databaseTransaction +import org.jetbrains.exposed.sql.Database import java.util.* /** * This class observes the vault and reflect current cash balances as exposed metrics in the monitoring service. */ -class CashBalanceAsMetricsObserver(val serviceHubInternal: ServiceHubInternal) { +class CashBalanceAsMetricsObserver(val serviceHubInternal: ServiceHubInternal, val database: Database) { init { // TODO: Need to consider failure scenarios. This needs to run if the TX is successfully recorded serviceHubInternal.vaultService.updates.subscribe { update -> @@ -29,13 +31,15 @@ class CashBalanceAsMetricsObserver(val serviceHubInternal: ServiceHubInternal) { // // Note: exported as pennies. val m = serviceHubInternal.monitoringService.metrics - for ((key, value) in vault.cashBalances) { - val metric = balanceMetrics.getOrPut(key) { - val newMetric = BalanceMetric() - m.register("VaultBalances.${key}Pennies", newMetric) - newMetric + databaseTransaction(database) { + for ((key, value) in vault.cashBalances) { + val metric = balanceMetrics.getOrPut(key) { + val newMetric = BalanceMetric() + m.register("VaultBalances.${key}Pennies", newMetric) + newMetric + } + metric.pennies = value.quantity } - metric.pennies = value.quantity } } } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index a03b65cbf7..72946d5272 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -12,6 +12,7 @@ import net.corda.core.node.ServiceHub import net.corda.core.node.services.Vault import net.corda.core.node.services.VaultService import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.core.tee import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.loggerFor @@ -80,6 +81,9 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT } val _updatesPublisher = PublishSubject.create() + val _rawUpdatesPublisher = PublishSubject.create() + // For use during publishing only. + val updatesPublisher: rx.Observer get() = _updatesPublisher.bufferUntilDatabaseCommit().tee(_rawUpdatesPublisher) fun allUnconsumedStates(): Iterable> { // Order by txhash for if and when transaction storage has some caching. @@ -104,6 +108,9 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT override val currentVault: Vault get() = mutex.locked { Vault(allUnconsumedStates()) } + override val rawUpdates: Observable + get() = mutex.locked { _rawUpdatesPublisher } + override val updates: Observable get() = mutex.locked { _updatesPublisher } @@ -127,7 +134,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT if (netDelta != Vault.NoUpdate) { mutex.locked { recordUpdate(netDelta) - _updatesPublisher.onNext(netDelta) + updatesPublisher.onNext(netDelta) } } return currentVault diff --git a/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt b/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt index 703773abe8..c110ae247a 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt @@ -7,9 +7,13 @@ import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.SecureHash import net.corda.core.crypto.parsePublicKeyBase58 import net.corda.core.crypto.toBase58String +import net.corda.node.utilities.StrandLocalTransactionManager.Boundary import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.TransactionInterface import org.jetbrains.exposed.sql.transactions.TransactionManager +import rx.Observable +import rx.subjects.PublishSubject +import rx.subjects.UnicastSubject import java.io.Closeable import java.security.PublicKey import java.sql.Connection @@ -18,6 +22,7 @@ import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneOffset import java.util.* +import java.util.concurrent.ConcurrentHashMap /** * Table prefix for all tables owned by the node module. @@ -66,12 +71,19 @@ fun isolatedTransaction(database: Database, block: Transaction.() -> T): T { * over each other. So here we use a companion object to hold them as [ThreadLocal] and [StrandLocalTransactionManager] * is otherwise effectively stateless so it's replacement does not matter. The [ThreadLocal] is then set correctly and * explicitly just prior to initiating a transaction in [databaseTransaction] and [createDatabaseTransaction] above. + * + * The [StrandLocalTransactionManager] instances have an [Observable] of the transaction close [Boundary]s which + * facilitates the use of [Observable.afterDatabaseCommit] to create event streams that only emit once the database + * transaction is closed and the data has been persisted and becomes visible to other observers. */ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionManager { companion object { + private val TX_ID = Key() + private val threadLocalDb = ThreadLocal() private val threadLocalTx = ThreadLocal() + private val databaseToInstance = ConcurrentHashMap() fun setThreadLocalTx(tx: Transaction?): Pair { val oldTx = threadLocalTx.get() @@ -89,10 +101,21 @@ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionMan set(value: Database) { threadLocalDb.set(value) } + + val transactionId: UUID + get() = threadLocalTx.get()?.getUserData(TX_ID) ?: throw IllegalStateException("Was expecting to find transaction set on current strand: ${Strand.currentStrand()}") + + val manager: StrandLocalTransactionManager get() = databaseToInstance[database]!! + + val transactionBoundaries: PublishSubject get() = manager._transactionBoundaries } + + data class Boundary(val txId: UUID) + + private val _transactionBoundaries = PublishSubject.create() + init { - database = initWithDatabase // Found a unit test that was forgetting to close the database transactions. When you close() on the top level // database transaction it will reset the threadLocalTx back to null, so if it isn't then there is still a // databae transaction open. The [databaseTransaction] helper above handles this in a finally clause for you @@ -100,16 +123,23 @@ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionMan if (threadLocalTx.get() != null) { throw IllegalStateException("Was not expecting to find existing database transaction on current strand when setting database: ${Strand.currentStrand()}, ${threadLocalTx.get()}") } + database = initWithDatabase + databaseToInstance[database] = this } - override fun newTransaction(isolation: Int): Transaction = Transaction(StrandLocalTransaction(database, isolation, threadLocalTx)).apply { - threadLocalTx.set(this) + override fun newTransaction(isolation: Int): Transaction { + val impl = StrandLocalTransaction(database, isolation, threadLocalTx, transactionBoundaries) + return Transaction(impl).apply { + threadLocalTx.set(this) + putUserData(TX_ID, impl.id) + } } override fun currentOrNull(): Transaction? = threadLocalTx.get() // Direct copy of [ThreadLocalTransaction]. - private class StrandLocalTransaction(override val db: Database, isolation: Int, val threadLocal: ThreadLocal) : TransactionInterface { + private class StrandLocalTransaction(override val db: Database, isolation: Int, val threadLocal: ThreadLocal, val transactionBoundaries: PublishSubject) : TransactionInterface { + val id = UUID.randomUUID() override val connection: Connection by lazy(LazyThreadSafetyMode.NONE) { db.connector().apply { @@ -133,13 +163,33 @@ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionMan override fun close() { connection.close() threadLocal.set(outerTransaction) + if (outerTransaction == null) { + transactionBoundaries.onNext(Boundary(id)) + } } } } +/** + * Buffer observations until after the current database transaction has been closed. Observations are never + * dropped, simply delayed. + * + * Primarily for use by component authors to publish observations during database transactions without racing against + * closing the database transaction. + * + * For examples, see the call hierarchy of this function. + */ +fun rx.Observer.bufferUntilDatabaseCommit(): rx.Observer { + val currentTxId = StrandLocalTransactionManager.transactionId + val databaseTxBoundary: Observable = StrandLocalTransactionManager.transactionBoundaries.filter { it.txId == currentTxId }.first() + val subject = UnicastSubject.create() + subject.delaySubscription(databaseTxBoundary).subscribe(this) + databaseTxBoundary.doOnCompleted { subject.onCompleted() } + return subject +} + // Composite columns for use with below Exposed helpers. data class PartyColumns(val name: Column, val owningKey: Column) - data class StateRefColumns(val txId: Column, val index: Column) data class TxnNoteColumns(val txId: Column, val note: Column) diff --git a/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt index b474ce1138..1f8fd97558 100644 --- a/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt @@ -8,6 +8,7 @@ import com.typesafe.config.ConfigFactory import net.corda.core.crypto.composite import net.corda.core.crypto.generateKeyPair import net.corda.core.messaging.Message +import net.corda.core.messaging.RPCOps import net.corda.core.messaging.createMessage import net.corda.core.node.services.DEFAULT_SESSION_ID import net.corda.core.utilities.LogHelper @@ -16,7 +17,6 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.NodeMessagingClient -import net.corda.core.messaging.RPCOps import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.PersistentUniquenessProvider diff --git a/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt index de5d6b4e96..9fbc81b658 100644 --- a/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt @@ -5,6 +5,7 @@ import net.corda.core.crypto.generateKeyPair import net.corda.core.getOrThrow import net.corda.core.node.services.ServiceInfo import net.corda.node.services.network.NetworkMapService +import net.corda.node.utilities.databaseTransaction import net.corda.testing.expect import net.corda.testing.node.MockNetwork import org.junit.Test @@ -30,7 +31,9 @@ class InMemoryNetworkMapCacheTest { // Node A currently knows only about itself, so this returns node A assertEquals(nodeA.netMapCache.getNodeByCompositeKey(keyPair.public.composite), nodeA.info) - nodeA.netMapCache.addNode(nodeB.info) + databaseTransaction(nodeA.database) { + nodeA.netMapCache.addNode(nodeB.info) + } // Now both nodes match, so it throws an error expect { nodeA.netMapCache.getNodeByCompositeKey(keyPair.public.composite) diff --git a/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapServiceTest.kt index be387b6131..b0a0ebab1a 100644 --- a/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapServiceTest.kt @@ -16,6 +16,7 @@ import net.corda.node.services.network.NetworkMapService.Companion.REGISTER_FLOW import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_FLOW_TOPIC import net.corda.node.services.network.NodeRegistration import net.corda.node.utilities.AddOrRemove +import net.corda.node.utilities.databaseTransaction import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import org.junit.Before @@ -201,7 +202,9 @@ class InMemoryNetworkMapServiceTest : AbstractNetworkMapServiceTest() { fun success() { val (mapServiceNode, registerNode) = network.createTwoNodes() val service = mapServiceNode.inNodeNetworkMapService!! as InMemoryNetworkMapService - success(mapServiceNode, registerNode, { service }, { }) + databaseTransaction(mapServiceNode.database) { + success(mapServiceNode, registerNode, { service }, { }) + } } @Test diff --git a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt new file mode 100644 index 0000000000..6368c33bb8 --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt @@ -0,0 +1,142 @@ +package net.corda.node.utilities + +import com.google.common.util.concurrent.SettableFuture +import net.corda.core.tee +import net.corda.testing.node.makeTestDataSourceProperties +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.exposed.sql.transactions.TransactionManager +import org.junit.Test +import rx.Observable +import rx.subjects.PublishSubject + +class ObservablesTests { + + private fun isInDatabaseTransaction(): Boolean = (TransactionManager.currentOrNull() != null) + + @Test + fun `bufferUntilDatabaseCommit delays until transaction closed`() { + val (toBeClosed, database) = configureDatabase(makeTestDataSourceProperties()) + + val subject = PublishSubject.create() + val observable: Observable = subject + + val firstEvent = SettableFuture.create>() + val secondEvent = SettableFuture.create>() + + observable.first().subscribe { firstEvent.set(it to isInDatabaseTransaction()) } + observable.skip(1).first().subscribe { secondEvent.set(it to isInDatabaseTransaction()) } + + databaseTransaction(database) { + val delayedSubject = subject.bufferUntilDatabaseCommit() + assertThat(subject).isNotEqualTo(delayedSubject) + delayedSubject.onNext(0) + subject.onNext(1) + assertThat(firstEvent.isDone).isTrue() + assertThat(secondEvent.isDone).isFalse() + } + assertThat(secondEvent.isDone).isTrue() + + assertThat(firstEvent.get()).isEqualTo(1 to true) + assertThat(secondEvent.get()).isEqualTo(0 to false) + + toBeClosed.close() + } + + @Test + fun `bufferUntilDatabaseCommit delays until transaction closed repeatable`() { + val (toBeClosed, database) = configureDatabase(makeTestDataSourceProperties()) + + val subject = PublishSubject.create() + val observable: Observable = subject + + val firstEvent = SettableFuture.create>() + val secondEvent = SettableFuture.create>() + + observable.first().subscribe { firstEvent.set(it to isInDatabaseTransaction()) } + observable.skip(1).first().subscribe { secondEvent.set(it to isInDatabaseTransaction()) } + + databaseTransaction(database) { + val delayedSubject = subject.bufferUntilDatabaseCommit() + assertThat(subject).isNotEqualTo(delayedSubject) + delayedSubject.onNext(0) + assertThat(firstEvent.isDone).isFalse() + assertThat(secondEvent.isDone).isFalse() + } + assertThat(firstEvent.isDone).isTrue() + assertThat(firstEvent.get()).isEqualTo(0 to false) + assertThat(secondEvent.isDone).isFalse() + + databaseTransaction(database) { + val delayedSubject = subject.bufferUntilDatabaseCommit() + assertThat(subject).isNotEqualTo(delayedSubject) + delayedSubject.onNext(1) + assertThat(secondEvent.isDone).isFalse() + } + assertThat(secondEvent.isDone).isTrue() + assertThat(secondEvent.get()).isEqualTo(1 to false) + + toBeClosed.close() + } + + @Test + fun `tee correctly copies observations to multiple observers`() { + + val subject1 = PublishSubject.create() + val subject2 = PublishSubject.create() + val subject3 = PublishSubject.create() + + val event1 = SettableFuture.create() + val event2 = SettableFuture.create() + val event3 = SettableFuture.create() + + subject1.subscribe { event1.set(it) } + subject2.subscribe { event2.set(it) } + subject3.subscribe { event3.set(it) } + + val tee = subject1.tee(subject2, subject3) + tee.onNext(0) + + assertThat(event1.isDone).isTrue() + assertThat(event2.isDone).isTrue() + assertThat(event3.isDone).isTrue() + assertThat(event1.get()).isEqualTo(0) + assertThat(event2.get()).isEqualTo(0) + assertThat(event3.get()).isEqualTo(0) + + tee.onCompleted() + assertThat(subject1.hasCompleted()).isTrue() + assertThat(subject2.hasCompleted()).isTrue() + assertThat(subject3.hasCompleted()).isTrue() + } + + @Test + fun `combine tee and bufferUntilDatabaseCommit`() { + val (toBeClosed, database) = configureDatabase(makeTestDataSourceProperties()) + + val subject = PublishSubject.create() + val teed = PublishSubject.create() + + val observable: Observable = subject + + val firstEvent = SettableFuture.create>() + val teedEvent = SettableFuture.create>() + + observable.first().subscribe { firstEvent.set(it to isInDatabaseTransaction()) } + + teed.first().subscribe { teedEvent.set(it to isInDatabaseTransaction()) } + + databaseTransaction(database) { + val delayedSubject = subject.bufferUntilDatabaseCommit().tee(teed) + assertThat(subject).isNotEqualTo(delayedSubject) + delayedSubject.onNext(0) + assertThat(firstEvent.isDone).isFalse() + assertThat(teedEvent.isDone).isTrue() + } + assertThat(firstEvent.isDone).isTrue() + + assertThat(firstEvent.get()).isEqualTo(0 to false) + assertThat(teedEvent.get()).isEqualTo(0 to true) + + toBeClosed.close() + } +} \ No newline at end of file From 2f613409452a80e837e8f7b5845eb5c2e8b9fe69 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Thu, 8 Dec 2016 16:58:22 +0000 Subject: [PATCH 31/37] Fixed review comments. --- docs/source/publishing-corda.rst | 2 +- gradle-plugins/publish-utils/README.rst | 6 +++--- node/capsule/build.gradle | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/publishing-corda.rst b/docs/source/publishing-corda.rst index 2c07f1b598..ab04bcf3e9 100644 --- a/docs/source/publishing-corda.rst +++ b/docs/source/publishing-corda.rst @@ -69,7 +69,7 @@ Publishing `````````` 1. Publish to Bintray -2. Navigate to the project you wish to publist +2. Navigate to the project you wish to publish 3. Click "Maven Central" 4. Enter your Sonatype credentials to publish a new version diff --git a/gradle-plugins/publish-utils/README.rst b/gradle-plugins/publish-utils/README.rst index 3399688602..3ca1c2febf 100644 --- a/gradle-plugins/publish-utils/README.rst +++ b/gradle-plugins/publish-utils/README.rst @@ -68,16 +68,16 @@ Next you must setup the general bintray configuration you wish to use project wi **Publications** This plugin assumes, by default, that publications match the name of the project. This means, by default, you can -just list the names of the projects you wish to publish (eg; to publish `test:myapp` you need `publications = ['myapp']`. +just list the names of the projects you wish to publish (e.g. to publish `test:myapp` you need `publications = ['myapp']`. If a project requires a different name you can configure it *per project* with the project configuration block. -The project configuration block has the following structure; +The project configuration block has the following structure: .. code-block:: text publish { name = 'non-default-project-name' - disableDefaultJar = false // set to true to disable the default JAR being created (eg; when creating a fat JAR) + disableDefaultJar = false // set to true to disable the default JAR being created (e.g. when creating a fat JAR) } **Artifacts** diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle index c8e64ba2e4..4598daecbd 100644 --- a/node/capsule/build.gradle +++ b/node/capsule/build.gradle @@ -48,7 +48,7 @@ dependencies { compile project(':node') } -task buildCordaJAR(type: FatCapsule, dependsOn: [':node:jar', 'buildCertSigningRequestUtilityJAR']) { +task buildCordaJAR(type: FatCapsule, dependsOn: ['buildCertSigningRequestUtilityJAR']) { applicationClass 'net.corda.node.MainKt' archiveName "corda-${corda_version}.jar" applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class', 'config/dev/log4j2.xml') @@ -61,7 +61,7 @@ task buildCordaJAR(type: FatCapsule, dependsOn: [':node:jar', 'buildCertSigningR } } -task buildCertSigningRequestUtilityJAR(type: FatCapsule, dependsOn: [':node:jar']) { +task buildCertSigningRequestUtilityJAR(type: FatCapsule) { applicationClass 'net.corda.node.utilities.certsigning.CertificateSignerKt' archiveName 'certSigningRequestUtility.jar' capsuleManifest { From 453f7cd2235c9d4da8065fa48fcbace9048a6111 Mon Sep 17 00:00:00 2001 From: Jose Coll Date: Fri, 18 Nov 2016 17:51:12 +0000 Subject: [PATCH 32/37] Bank of Corda demo - Issuer of Cash Resolve BankOfCorda through NMS in protocol Fixes following Integration testing. Register custom RPC Kryo classes. Protocol -> Flow renaming Bank of Corda demo - Issuer of Cash Resolve BankOfCorda through NMS in protocol Fixes following Integration testing. Protocol -> Flow renaming Addressed all comments in PR review. Removed bank lines. Updated minor inconsistency in README.md All protocol references changed to flow. changed protocol -> flow in TODO comment. changed startProtocolPermission -> startFlowPermission in README.md Added transaction id to IssuerFlow Success response. Removed explicit call to record Cash Move transaction (as already recorded in subflow) Removed quasar dependency. Addressed comment in PR. Updated to use CompositeKey. Added arguments to pass in Currency and Amount. Updated run configurations to pass in Currency and Amount values Added additional parameter to IssuerFlow request: issueToPartyReference Added Vault updates verification in RPC Integration test. Fixed RPC Integration test (Vault assertions) Updated run-time dependencies in line with other demos. Applied changes following PR review (exception handling, party resolution handling, docs) Updated gradle client run configs with new parameters. Main driver app now uses standard out for display (was using logger info() but nothing was being displayed because of restrictive config) Fixed formatting display problems. Updated Web Api code to use new CordaRPCOps interface (and new partyFromName() exposed method) Removed unused import. --- .../Bank_of_Corda_Demo__Run_Issuer.xml | 15 ++ ...Bank_of_Corda_Demo__Run_RPC_Cash_Issue.xml | 15 ++ ...Bank_of_Corda_Demo__Run_Web_Cash_Issue.xml | 15 ++ docs/source/running-the-demos.rst | 54 +++++++ samples/README.md | 3 +- samples/bank-of-corda-demo/README.md | 57 +++++++ samples/bank-of-corda-demo/build.gradle | 148 ++++++++++++++++++ samples/bank-of-corda-demo/gradle.properties | 2 + .../net/corda/bank/BankOfCordaHttpAPITest.kt | 20 +++ .../corda/bank/BankOfCordaRPCClientTest.kt | 86 ++++++++++ .../net/corda/bank/BankOfCordaDriver.kt | 93 +++++++++++ .../corda/bank/api/BankOfCordaClientApi.kt | 48 ++++++ .../net/corda/bank/api/BankOfCordaWebApi.kt | 57 +++++++ .../kotlin/net/corda/bank/api/Constants.kt | 21 +++ .../kotlin/net/corda/bank/flow/IssuerFlow.kt | 109 +++++++++++++ .../corda/bank/plugin/BankOfCordaPlugin.kt | 19 +++ .../net.corda.core.node.CordaPluginRegistry | 2 + .../net/corda/bank/flow/IssuerFlowTest.kt | 74 +++++++++ settings.gradle | 3 +- 19 files changed, 839 insertions(+), 2 deletions(-) create mode 100644 .idea/runConfigurations/Bank_of_Corda_Demo__Run_Issuer.xml create mode 100644 .idea/runConfigurations/Bank_of_Corda_Demo__Run_RPC_Cash_Issue.xml create mode 100644 .idea/runConfigurations/Bank_of_Corda_Demo__Run_Web_Cash_Issue.xml create mode 100644 samples/bank-of-corda-demo/README.md create mode 100644 samples/bank-of-corda-demo/build.gradle create mode 100644 samples/bank-of-corda-demo/gradle.properties create mode 100644 samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt create mode 100644 samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/Constants.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/flow/IssuerFlow.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/plugin/BankOfCordaPlugin.kt create mode 100644 samples/bank-of-corda-demo/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry create mode 100644 samples/bank-of-corda-demo/src/test/kotlin/net/corda/bank/flow/IssuerFlowTest.kt diff --git a/.idea/runConfigurations/Bank_of_Corda_Demo__Run_Issuer.xml b/.idea/runConfigurations/Bank_of_Corda_Demo__Run_Issuer.xml new file mode 100644 index 0000000000..314089c670 --- /dev/null +++ b/.idea/runConfigurations/Bank_of_Corda_Demo__Run_Issuer.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Bank_of_Corda_Demo__Run_RPC_Cash_Issue.xml b/.idea/runConfigurations/Bank_of_Corda_Demo__Run_RPC_Cash_Issue.xml new file mode 100644 index 0000000000..dfe14546a3 --- /dev/null +++ b/.idea/runConfigurations/Bank_of_Corda_Demo__Run_RPC_Cash_Issue.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Bank_of_Corda_Demo__Run_Web_Cash_Issue.xml b/.idea/runConfigurations/Bank_of_Corda_Demo__Run_Web_Cash_Issue.xml new file mode 100644 index 0000000000..ce474a1406 --- /dev/null +++ b/.idea/runConfigurations/Bank_of_Corda_Demo__Run_Web_Cash_Issue.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/docs/source/running-the-demos.rst b/docs/source/running-the-demos.rst index a1a3939591..d27907da4d 100644 --- a/docs/source/running-the-demos.rst +++ b/docs/source/running-the-demos.rst @@ -13,6 +13,8 @@ so far. We have: 5. The SIMM valuation demo, a large demo which shows two nodes agreeing on a portfolio and valuing the initial margin using the Standard Initial Margin Model. 6. The distributed notary demo, which demonstrates a single node getting multiple transactions notarised by a distributed (Raft-based) notary. +7. The Bank of Corda demo, which demonstrates a node acting as an issuer of assets (the Bank of Corda) and remote client + applications requesting issuance (via RPC, HTTP) of some cash on behalf of a node called Big Corporation. .. note:: If any demos don't work please jump on our mailing list and let us know. @@ -156,6 +158,58 @@ by using the H2 web console: - The committed states are stored in the ``NOTARY_COMMITTED_STATES`` table. Note that the raw data is not human-readable, but we're only interested in the row count for this demo. +Bank Of Corda demo +------------------ + +This demo brings up three nodes: a notary, a node acting as the Bank of Corda that accepts requests for issuance of some asset +and a node acting as Big Corporation which requests issuance of an asset (cash in this example). +Upon receipt of a request the Bank of Corda node self-issues the asset and then transfers ownership to the requester +after successful notarisation and recording of the issue transaction on the ledger. + +.. note:: The Bank of Corda is somewhat like the "Bitcoin faucet", that used to dispense free bitcoins to developers for + testing and experimentation purposes. + +To run from the command line (recommended for Mac/UNIX users!): + +1. Run ``./gradlew samples:bank-of-corda-demo:deployNodes`` to create a set of configs and installs under ``samples/bank-of-corda-demo/build/nodes`` +2. Run ``./samples/bank-of-corda-demo/build/nodes/runnodes`` to open up three new terminal tabs/windows with the three nodes. + +.. note:: to verify the Bank of Corda node is alive and running navigate to the following URL + http://localhost:10005/api/bank/date + +3. Run ``./gradlew samples:bank-of-corda-demo:runRPCCashIssue`` in another terminal window to trigger a cash issuance request +4. Run ``./gradlew samples:bank-of-corda-demo:runWebCashIssue`` in another terminal window to trigger another cash issuance request + Now look at the other windows to see the output of the demo. + +Or you can run them from inside IntelliJ as follows: + +1. Open the Corda project in IntelliJ and run the "Install" configuration +2. Open the Corda samples project in IntelliJ and run the "Bank Of Corda Demo: Run Issuer" configuration +3. Run "Bank Of Corda Demo: Run RPC Cash Issue" - requests issuance of some cash on behalf of Big Corporation via RPC +4. Run "Bank Of Corda Demo: Run Web Cash Issue" - requests issuance of some cash on behalf of Big Corporation via HTTP + +In the "Bank Of Corda Demo: Run Issuer" window you should see the following information lines displayed: + +- Awaiting issuance request +- Self issuing asset +- Transferring asset to issuance requester +- Confirming asset issuance to requester + +In the the client issue request window you should see the following printed: + +- Successfully processed Cash Issue request + +Launch the Explorer application to visualize the issuance and transfer of cash on each node: + + ``./gradlew tools:explorer:run`` + +And use the following logon details: + +- for the Bank of Corda node specify localhost, port 10004, username user1, password test +- for the Big Corporation node specify localhost, port 10006, username user1, password test + +See https://docs.corda.net/node-explorer.html for further details on usage. + SIMM and Portfolio Demo - aka the Initial Margin Agreement Demo --------------------------------------------------------------- diff --git a/samples/README.md b/samples/README.md index 2c9d3fbaa2..fd33b5e052 100644 --- a/samples/README.md +++ b/samples/README.md @@ -7,4 +7,5 @@ Please refer to `README.md` in the individual project folders. There are the fo * **trader-demo** A simple driver for exercising the two party trading flow. In this scenario, a buyer wants to purchase some commercial paper by swapping his cash for commercial paper. The seller learns that the buyer exists, and sends them a message to kick off the trade. The seller, having obtained his CP, then quits and the buyer goes back to waiting. The buyer will sell as much CP as he can! **We recommend starting with this demo.** * **Network-visualiser** A tool that uses a simulation to visualise the interaction and messages between nodes on the Corda network. Currently only works for the IRS demo. * **simm-valudation-demo** A demo showing two nodes reaching agreement on the valuation of a derivatives portfolio. -* **raft-notary-demo** A simple demonstration of a node getting multiple transactions notarised by a distributed (Raft-based) notary. \ No newline at end of file +* **raft-notary-demo** A simple demonstration of a node getting multiple transactions notarised by a distributed (Raft-based) notary. +* **bank-of-corda-demo** A demo showing a node acting as an issuer of fungible assets (initially Cash) \ No newline at end of file diff --git a/samples/bank-of-corda-demo/README.md b/samples/bank-of-corda-demo/README.md new file mode 100644 index 0000000000..89bf18fc0e --- /dev/null +++ b/samples/bank-of-corda-demo/README.md @@ -0,0 +1,57 @@ +# Bank of Corda demo +Please see docs/build/html/running-the-demos.html + +This program simulates the role of an asset issuing authority (eg. central bank for cash) by accepting requests +from third parties to issue some quantity of an asset and transfer that ownership to the requester. +The issuing authority accepts requests via the [IssuerFlow] flow, self-issues the asset and transfers +ownership to the issue requester. Notarisation and signing form part of the flow. + +The requesting party can be a CorDapp (running locally or remotely to the Bank of Corda node), a remote RPC client or +a Web Client. + +## Prerequisites + +You will need to have [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) +installed and available on your path. + +## Getting Started + +1. Launch the Bank of Corda node (and associated Notary) by running: +[BankOfCordaDriver] --role ISSUER +(to validate your Node is running you can try navigating to this sample link: http://localhost:10005/api/bank/date) + +Each of the following commands will launch a separate Node called Big Corporation which will become the owner +of some Cash following an issue request: + +2. Run the Bank of Corda Client driver (to simulate a web issue requester) by running: +[BankOfCordaDriver] --role ISSUE_CASH_WEB +This demonstrates a remote application acting on behalf of the Big Corporation and communicating directly with the +Bank of Corda node via HTTP to request issuance of some cash. + +3. Run the Bank of Corda Client driver (to simulate an RPC issue requester) by running: +[BankOfCordaDriver] --role ISSUE_CASH_RPC +Similar to 3 above, but using RPC as the remote communications mechanism. + +## Developer notes + +Testing of the Bank of Corda application is demonstrated at two levels: +1. Unit testing the flow uses the [LedgerDSL] and [MockServices] +2. Integration testing via RPC and HTTP uses the [Driver] DSL to launch standalone node instances + +Security +The RPC API requires a client to pass in user credentials: + client.start("user1","test") +which are validated on the Bank of Corda node against those configured at node startup: + User("user1", "test", permissions = setOf(startFlowPermission())) + startNode("BankOfCorda", rpcUsers = listOf(user)) + +Notary +We are using a [SimpleNotaryService] in this example, but could easily switch to a [ValidatingNotaryService] + +## Future + +The Bank of Corda node will become an integral part of other Corda samples that require initial issuance of some asset. + +## Further Reading + +Tutorials and developer docs for Cordapps and Corda are [here](https://docs.corda.net/). \ No newline at end of file diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle new file mode 100644 index 0000000000..89796c27ba --- /dev/null +++ b/samples/bank-of-corda-demo/build.gradle @@ -0,0 +1,148 @@ +apply plugin: 'java' +apply plugin: 'kotlin' +apply plugin: 'idea' +apply plugin: 'net.corda.plugins.quasar-utils' +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.cordformation' +apply plugin: 'maven-publish' + +repositories { + mavenLocal() + mavenCentral() + jcenter() + maven { + url 'https://dl.bintray.com/kotlin/exposed' + } +} + +sourceSets { + main { + resources { + srcDir "../../config/dev" + } + } + test { + resources { + srcDir "../../config/test" + } + } + integrationTest { + kotlin { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + srcDir file('src/integration-test/kotlin') + } + } +} + +configurations { + integrationTestCompile.extendsFrom testCompile + integrationTestRuntime.extendsFrom testRuntime +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + testCompile group: 'junit', name: 'junit', version: '4.11' + + // Corda integration dependencies + runtime project(path: ":node", configuration: 'runtimeArtifacts') + compile project(':core') + compile project(':client') + compile project(':node') + compile project(':finance') + compile project(':test-utils') + + // Javax is required for webapis + compile "org.glassfish.jersey.core:jersey-server:${jersey_version}" +} + +task deployNodes(type: net.corda.plugins.Cordform, dependsOn: [':install', 'build']) { + directory "./build/nodes" + // This name "Notary" is hard-coded into BankOfCordaClientApi so if you change it here, change it there too. + // In this demo the node that runs a standalone notary also acts as the network map server. + networkMap "Notary" + node { + name "Notary" + dirName "notary" + nearestCity "London" + advertisedServices = ["corda.notary.validating"] + artemisPort 10002 + webPort 10003 + cordapps = [] + } + node { + name "BankOfCorda" + dirName "node-bank-of-corda" + nearestCity "London" + advertisedServices = [] + artemisPort 10004 + webPort 10005 + cordapps = [] +// TODO: task needs to parse this item when generating node.conf +// rpcUsers : [ +// { user=user1, password=test, permissions=[ StartFlow.net.corda.bank.flow.IssuerFlow$IssuanceRequester ] } +// ] + } + node { + name "BigCorporation" + dirName "node-big-corp" + nearestCity "New York" + advertisedServices = [] + artemisPort 10006 + webPort 10007 + cordapps = [] + } +} + +task integrationTest(type: Test, dependsOn: []) { + testClassesDir = sourceSets.integrationTest.output.classesDir + classpath = sourceSets.integrationTest.runtimeClasspath +} + +idea { + module { + downloadJavadoc = true // defaults to false + downloadSources = true + } +} + +publishing { + publications { + jarAndSources(MavenPublication) { + from components.java + artifactId 'bankofcorda' + + artifact sourceJar + artifact javadocJar + } + } +} + +task runIssuer(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + main = 'net.corda.bank.BankOfCordaDriverKt' + args '--role' + args 'ISSUER' +} + +task runRPCCashIssue(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + main = 'net.corda.bank.BankOfCordaDriverKt' + args '--role' + args 'ISSUE_CASH_RPC' + args '--quantity' + args 20000 + args '--currency' + args 'USD' +} + +task runWebCashIssue(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + main = 'net.corda.bank.BankOfCordaDriverKt' + args '--role' + args 'ISSUE_CASH_WEB' + args '--quantity' + args 30000 + args '--currency' + args 'GBP' +} \ No newline at end of file diff --git a/samples/bank-of-corda-demo/gradle.properties b/samples/bank-of-corda-demo/gradle.properties new file mode 100644 index 0000000000..97464c746e --- /dev/null +++ b/samples/bank-of-corda-demo/gradle.properties @@ -0,0 +1,2 @@ +name = BankOfCorda +kotlin.incremental=false \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt new file mode 100644 index 0000000000..035d80b26f --- /dev/null +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -0,0 +1,20 @@ +package net.corda.bank + +import net.corda.bank.api.BankOfCordaClientApi +import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams +import net.corda.core.node.services.ServiceInfo +import net.corda.node.driver.driver +import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.testing.getHostAndPort +import org.junit.Test + +class BankOfCordaHttpAPITest { + @Test fun `test issuer flow via Http`() { + driver(dsl = { + val nodeBankOfCorda = startNode("BankOfCorda", setOf(ServiceInfo(SimpleNotaryService.type))).get() + val nodeBankOfCordaApiAddr = nodeBankOfCorda.config.getHostAndPort("webAddress") + startNode("BigCorporation").get() + assert(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", "BigCorporation", "1", "BankOfCorda"))) + }, isDebug = true) + } +} \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt new file mode 100644 index 0000000000..26eff429f7 --- /dev/null +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -0,0 +1,86 @@ +package net.corda.bank + +import net.corda.bank.api.BOC_ISSUER_PARTY_REF +import net.corda.bank.flow.IssuerFlow.IssuanceRequester +import net.corda.core.contracts.DOLLARS +import net.corda.core.messaging.startFlow +import net.corda.core.node.services.ServiceInfo +import net.corda.core.transactions.SignedTransaction +import net.corda.node.driver.driver +import net.corda.node.services.User +import net.corda.node.services.config.configureTestSSL +import net.corda.node.services.messaging.CordaRPCClient +import net.corda.node.services.startFlowPermission +import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.testing.expect +import net.corda.testing.expectEvents +import net.corda.testing.getHostAndPort +import net.corda.testing.sequence +import org.junit.Test +import kotlin.test.assertTrue + +class BankOfCordaRPCClientTest { + + @Test fun `test issuer flow via RPC`() { + driver(dsl = { + val user = User("user1", "test", permissions = setOf(startFlowPermission())) + val nodeBankOfCorda = startNode("BankOfCorda", setOf(ServiceInfo(SimpleNotaryService.type)), arrayListOf(user)).get() + val nodeBankOfCordaApiAddr = nodeBankOfCorda.config.getHostAndPort("artemisAddress") + val bankOfCordaParty = nodeBankOfCorda.nodeInfo.legalIdentity + val nodeBigCorporation = startNode("BigCorporation", rpcUsers = arrayListOf(user)).get() + val bigCorporationParty = nodeBigCorporation.nodeInfo.legalIdentity + + // Bank of Corda RPC Client + val bocClient = CordaRPCClient(nodeBankOfCordaApiAddr, configureTestSSL()) + bocClient.start("user1","test") + val bocProxy = bocClient.proxy() + + // Big Corporation RPC Client + val bigCorpClient = CordaRPCClient(nodeBankOfCordaApiAddr, configureTestSSL()) + bigCorpClient.start("user1","test") + val bigCorpProxy = bigCorpClient.proxy() + + // Register for Bank of Corda Vault updates + val vaultUpdatesBoc = bocProxy.vaultAndUpdates().second + + // Register for Big Corporation Vault updates + val vaultUpdatesBigCorp = bigCorpProxy.vaultAndUpdates().second + + // Kick-off actual Issuer Flow + val result = bocProxy.startFlow(::IssuanceRequester, 1000.DOLLARS, bigCorporationParty, BOC_ISSUER_PARTY_REF, bankOfCordaParty).returnValue.toBlocking().first() + assertTrue { result is SignedTransaction } + + // Check Bank of Corda Vault Updates + vaultUpdatesBoc.expectEvents { + sequence( + // ISSUE + expect { update -> + require(update.consumed.size == 0) { update.consumed.size } + require(update.produced.size == 1) { update.produced.size } + }, + // MOVE + expect { update -> + require(update.consumed.size == 1) { update.consumed.size } + require(update.produced.size == 0) { update.produced.size } + } + ) + } + + // Check Big Corporation Vault Updates + vaultUpdatesBigCorp.expectEvents { + sequence( + // ISSUE + expect { update -> + require(update.consumed.size == 0) { update.consumed.size } + require(update.produced.size == 1) { update.produced.size } + }, + // MOVE + expect { update -> + require(update.consumed.size == 1) { update.consumed.size } + require(update.produced.size == 0) { update.produced.size } + } + ) + } + }, isDebug = true) + } +} \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt new file mode 100644 index 0000000000..816a557708 --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -0,0 +1,93 @@ +package net.corda.bank + +import com.google.common.net.HostAndPort +import joptsimple.OptionParser +import net.corda.bank.api.BankOfCordaClientApi +import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams +import net.corda.bank.flow.IssuerFlow +import net.corda.core.node.services.ServiceInfo +import net.corda.core.transactions.SignedTransaction +import net.corda.node.driver.driver +import net.corda.node.services.User +import net.corda.node.services.startFlowPermission +import net.corda.node.services.transactions.SimpleNotaryService +import kotlin.system.exitProcess + +/** + * This entry point allows for command line running of the Bank of Corda functions on nodes started by BankOfCordaDriver.kt. + */ +fun main(args: Array) { + BankOfCordaDriver().main(args) +} + +private class BankOfCordaDriver { + enum class Role { + ISSUE_CASH_RPC, + ISSUE_CASH_WEB, + ISSUER + } + + fun main(args: Array) { + val parser = OptionParser() + val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).describedAs("[ISSUER|ISSUE_CASH_RPC|ISSUE_CASH_WEB]") + val quantity = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java) + val currency = parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]") + val options = try { + parser.parse(*args) + } catch (e: Exception) { + println(e.message) + printHelp(parser) + exitProcess(1) + } + + // What happens next depends on the role. + // The ISSUER will launch a Bank of Corda node + // The ISSUE_CASH will request some Cash from the ISSUER on behalf of Big Corporation node + val role = options.valueOf(roleArg)!! + if (role == Role.ISSUER) { + driver(dsl = { + val user = User("user1", "test", permissions = setOf(startFlowPermission())) + startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.type))) + startNode("BankOfCorda", rpcUsers = listOf(user)) + startNode("BigCorporation") + waitForAllNodesToFinish() + }, isDebug = true) + } + else { + try { + val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), "BigCorporation", "1", "BankOfCorda") + when (role) { + Role.ISSUE_CASH_RPC -> { + println("Requesting Cash via RPC ...") + val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10004")).requestRPCIssue(requestParams) + if (result is SignedTransaction) + println("Success!! You transaction receipt is ${result.tx.id}") + } + Role.ISSUE_CASH_WEB -> { + println("Requesting Cash via Web ...") + val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10005")).requestWebIssue(requestParams) + if (result) + println("Successfully processed Cash Issue request") + } + Role.ISSUER -> {} + } + } + catch (e: Exception) { + printHelp(parser) + } + } + } + + fun printHelp(parser: OptionParser) { + println(""" + Usage: bank-of-corda --role ISSUER + bank-of-corda --role (ISSUE_CASH_RPC|ISSUE_CASH_WEB) --quantity --currency + + Please refer to the documentation in docs/build/index.html for more info. + + """.trimIndent()) + parser.printHelpOn(System.out) + } +} + + diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt new file mode 100644 index 0000000000..89e6d62ab9 --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -0,0 +1,48 @@ +package net.corda.bank.api + +import com.google.common.net.HostAndPort +import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams +import net.corda.bank.flow.IssuerFlow.IssuanceRequester +import net.corda.node.services.messaging.CordaRPCClient +import net.corda.core.contracts.Amount +import net.corda.core.contracts.currency +import net.corda.core.messaging.startFlow +import net.corda.core.serialization.OpaqueBytes +import net.corda.core.transactions.SignedTransaction +import net.corda.node.services.config.configureTestSSL +import net.corda.testing.http.HttpApi + +/** + * Interface for communicating with Bank of Corda node + */ +class BankOfCordaClientApi(val hostAndPort: HostAndPort) { + private val apiRoot = "api/bank" + /** + * HTTP API + */ + // TODO: security controls required + fun requestWebIssue(params: IssueRequestParams): Boolean { + val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot) + return api.postJson("issue-asset-request", params) + } + /** + * RPC API + */ + fun requestRPCIssue(params: IssueRequestParams): SignedTransaction { + val client = CordaRPCClient(hostAndPort, configureTestSSL()) + // TODO: privileged security controls required + client.start("user1","test") + val proxy = client.proxy() + + // Resolve parties via RPC + val issueToParty = proxy.partyFromName(params.issueToPartyName) + ?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service") + val issuerBankParty = proxy.partyFromName(params.issuerBankName) + ?: throw Exception("Unable to locate ${params.issuerBankName} in Network Map Service") + + val amount = Amount(params.amount, currency(params.currency)) + val issuerToPartyRef = OpaqueBytes.of(params.issueToPartyRefAsString.toByte()) + + return proxy.startFlow(::IssuanceRequester, amount, issueToParty, issuerToPartyRef, issuerBankParty).returnValue.toBlocking().first() + } +} diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt new file mode 100644 index 0000000000..edaea3373d --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -0,0 +1,57 @@ +package net.corda.bank.api + +import net.corda.bank.flow.IssuerFlow.IssuanceRequester +import net.corda.core.contracts.Amount +import net.corda.core.contracts.currency +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.messaging.startFlow +import net.corda.core.serialization.OpaqueBytes +import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.loggerFor +import java.time.LocalDateTime +import javax.ws.rs.* +import javax.ws.rs.core.MediaType +import javax.ws.rs.core.Response + +// API is accessible from /api/bank. All paths specified below are relative to it. +@Path("bank") +class BankOfCordaWebApi(val rpc: CordaRPCOps) { + data class IssueRequestParams(val amount: Long, val currency: String, + val issueToPartyName: String, val issueToPartyRefAsString: String, + val issuerBankName: String) + private companion object { + val logger = loggerFor() + } + @GET + @Path("date") + @Produces(MediaType.APPLICATION_JSON) + fun getCurrentDate(): Any { + return mapOf("date" to LocalDateTime.now().toLocalDate()) + } + /** + * Request asset issuance + */ + @POST + @Path("issue-asset-request") + @Consumes(MediaType.APPLICATION_JSON) + fun issueAssetRequest(params: IssueRequestParams): Response { + // Resolve parties via RPC + val issueToParty = rpc.partyFromName(params.issueToPartyName) + ?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service") + val issuerBankParty = rpc.partyFromName(params.issuerBankName) + ?: throw Exception("Unable to locate ${params.issuerBankName} in Network Map Service") + + val amount = Amount(params.amount, currency(params.currency)) + val issuerToPartyRef = OpaqueBytes.of(params.issueToPartyRefAsString.toByte()) + + // invoke client side of Issuer Flow: IssuanceRequester + // The line below blocks and waits for the future to resolve. + val result = rpc.startFlow(::IssuanceRequester, amount, issueToParty, issuerToPartyRef, issuerBankParty).returnValue.toBlocking().first() + if (result is SignedTransaction) { + logger.info("Issue request completed successfully: ${params}") + return Response.status(Response.Status.CREATED).build() + } else { + return Response.status(Response.Status.BAD_REQUEST).build() + } + } +} \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/Constants.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/Constants.kt new file mode 100644 index 0000000000..d44d1577da --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/Constants.kt @@ -0,0 +1,21 @@ +package net.corda.bank.api + +import net.corda.core.crypto.CompositeKey +import net.corda.core.crypto.Party +import net.corda.core.crypto.composite +import net.corda.core.crypto.generateKeyPair +import net.corda.core.serialization.OpaqueBytes +import net.corda.testing.MEGA_CORP +import java.security.KeyPair +import java.security.PublicKey + +val defaultRef = OpaqueBytes.of(1) + +/* + * Bank Of Corda (BOC_ISSUER_PARTY) + */ +val BOC_KEY: KeyPair by lazy { generateKeyPair() } +val BOC_PUBKEY: CompositeKey get() = BOC_KEY.public.composite +val BOC_ISSUER_PARTY: Party get() = Party("BankOfCorda", BOC_PUBKEY) +val BOC_ISSUER_PARTY_AND_REF = BOC_ISSUER_PARTY.ref(defaultRef) +val BOC_ISSUER_PARTY_REF = BOC_ISSUER_PARTY_AND_REF.reference \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/flow/IssuerFlow.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/flow/IssuerFlow.kt new file mode 100644 index 0000000000..ccfe1b3bb1 --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/flow/IssuerFlow.kt @@ -0,0 +1,109 @@ +package net.corda.bank.flow + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.contracts.* +import net.corda.core.crypto.Party +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FlowLogic +import net.corda.core.node.NodeInfo +import net.corda.core.node.PluginServiceHub +import net.corda.core.serialization.OpaqueBytes +import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.ProgressTracker +import net.corda.flows.CashCommand +import net.corda.flows.CashFlow +import net.corda.flows.CashFlowResult +import java.util.* + +/** + * This flow enables a client to request issuance of some [FungibleAsset] from a + * server acting as an issuer (see [Issued]) of FungibleAssets. + * + * It is not intended for production usage, but rather for experimentation and testing purposes where it may be + * useful for creation of fake assets. + */ +object IssuerFlow { + data class IssuanceRequestState(val amount: Amount, val issueToParty: Party, val issuerPartyRef: OpaqueBytes) + + /** + * IssuanceRequester should be used by a client to ask a remote note to issue some [FungibleAsset] with the given details. + * Returns the transaction created by the Issuer to move the cash to the Requester. + */ + class IssuanceRequester(val amount: Amount, val issueToParty: Party, val issueToPartyRef: OpaqueBytes, + val issuerBankParty: Party): FlowLogic() { + @Suspendable + override fun call(): SignedTransaction { + val issueRequest = IssuanceRequestState(amount, issueToParty, issueToPartyRef) + return sendAndReceive(issuerBankParty, issueRequest).unwrap { it } + } + } + + /** + * Issuer refers to a Node acting as a Bank Issuer of [FungibleAsset], and processes requests from a [IssuanceRequester] client. + * Returns the generated transaction representing the transfer of the [Issued] [FungibleAsset] to the issue requester. + */ + class Issuer(val otherParty: Party): FlowLogic() { + override val progressTracker: ProgressTracker = Issuer.tracker() + companion object { + object AWAITING_REQUEST : ProgressTracker.Step("Awaiting issuance request") + object ISSUING : ProgressTracker.Step("Self issuing asset") + object TRANSFERRING : ProgressTracker.Step("Transferring asset to issuance requester") + object SENDING_CONFIRM : ProgressTracker.Step("Confirming asset issuance to requester") + fun tracker() = ProgressTracker(AWAITING_REQUEST, ISSUING, TRANSFERRING, SENDING_CONFIRM) + } + + @Suspendable + override fun call(): SignedTransaction { + progressTracker.currentStep = AWAITING_REQUEST + val issueRequest = receive(otherParty).unwrap { it } + // validate request inputs (for example, lets restrict the types of currency that can be issued) + require(listOf(USD, GBP, EUR, CHF).contains(issueRequest.amount.token)) { + logger.error("Currency must be one of USD, GBP, EUR, CHF") + } + // TODO: parse request to determine Asset to issue + val txn = issueCashTo(issueRequest.amount, issueRequest.issueToParty, issueRequest.issuerPartyRef) + progressTracker.currentStep = SENDING_CONFIRM + send(otherParty, txn) + return txn + } + + @Suspendable + private fun issueCashTo(amount: Amount, + issueTo: Party, issuerPartyRef: OpaqueBytes): SignedTransaction { + val notaryParty = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity + // invoke Cash subflow to issue Asset + progressTracker.currentStep = ISSUING + val bankOfCordaParty = serviceHub.myInfo.legalIdentity + val issueCashFlow = CashFlow(CashCommand.IssueCash(amount, issuerPartyRef, bankOfCordaParty, notaryParty)) + val resultIssue = subFlow(issueCashFlow) + // NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger) + if (resultIssue is CashFlowResult.Failed) { + logger.error("Problem issuing cash: ${resultIssue.message}") + throw Exception(resultIssue.message) + } + // now invoke Cash subflow to Move issued assetType to issue requester + progressTracker.currentStep = TRANSFERRING + val moveCashFlow = CashFlow(CashCommand.PayCash(amount.issuedBy(bankOfCordaParty.ref(issuerPartyRef)), issueTo)) + val resultMove = subFlow(moveCashFlow) + // NOTE: CashFlow PayCash calls FinalityFlow which performs a Broadcast (which stores a local copy of the txn to the ledger) + if (resultMove is CashFlowResult.Failed) { + logger.error("Problem transferring cash: ${resultMove.message}") + throw Exception(resultMove.message) + } + val txn = (resultMove as CashFlowResult.Success).transaction + txn?.let { + return txn + } + // NOTE: CashFlowResult.Success should always return a signedTransaction + throw Exception("Missing CashFlow transaction [${(resultMove)}]") + } + + class Service(services: PluginServiceHub) { + init { + services.registerFlowInitiator(IssuanceRequester::class) { + Issuer(it) + } + } + } + } +} \ No newline at end of file diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/plugin/BankOfCordaPlugin.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/plugin/BankOfCordaPlugin.kt new file mode 100644 index 0000000000..5c50b7d0a4 --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/plugin/BankOfCordaPlugin.kt @@ -0,0 +1,19 @@ +package net.corda.bank.plugin + +import net.corda.bank.api.BankOfCordaWebApi +import net.corda.bank.flow.IssuerFlow +import net.corda.core.contracts.Amount +import net.corda.core.crypto.Party +import net.corda.core.node.CordaPluginRegistry +import net.corda.core.serialization.OpaqueBytes +import java.util.function.Function + +class BankOfCordaPlugin : CordaPluginRegistry() { + // A list of classes that expose web APIs. + override val webApis = listOf(Function(::BankOfCordaWebApi)) + // A list of flow that are required for this cordapp + override val requiredFlows: Map> = + mapOf(IssuerFlow.IssuanceRequester::class.java.name to setOf(Amount::class.java.name, Party::class.java.name, OpaqueBytes::class.java.name, Party::class.java.name) + ) + override val servicePlugins = listOf(Function(IssuerFlow.Issuer::Service)) +} diff --git a/samples/bank-of-corda-demo/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry b/samples/bank-of-corda-demo/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry new file mode 100644 index 0000000000..b4dddc2d14 --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry @@ -0,0 +1,2 @@ +# Register a ServiceLoader service extending from net.corda.node.CordaPluginRegistry +net.corda.bank.plugin.BankOfCordaPlugin diff --git a/samples/bank-of-corda-demo/src/test/kotlin/net/corda/bank/flow/IssuerFlowTest.kt b/samples/bank-of-corda-demo/src/test/kotlin/net/corda/bank/flow/IssuerFlowTest.kt new file mode 100644 index 0000000000..5e03a4ea4e --- /dev/null +++ b/samples/bank-of-corda-demo/src/test/kotlin/net/corda/bank/flow/IssuerFlowTest.kt @@ -0,0 +1,74 @@ +package net.corda.bank.flow + +import com.google.common.util.concurrent.ListenableFuture +import net.corda.bank.api.BOC_ISSUER_PARTY +import net.corda.bank.api.BOC_KEY +import net.corda.bank.flow.IssuerFlow.IssuanceRequester +import net.corda.core.contracts.Amount +import net.corda.core.contracts.DOLLARS +import net.corda.core.contracts.PartyAndReference +import net.corda.core.contracts.currency +import net.corda.core.flows.FlowStateMachine +import net.corda.core.map +import net.corda.core.serialization.OpaqueBytes +import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.DUMMY_NOTARY +import net.corda.core.utilities.DUMMY_NOTARY_KEY +import net.corda.testing.MEGA_CORP +import net.corda.testing.MEGA_CORP_KEY +import net.corda.testing.initiateSingleShotFlow +import net.corda.testing.ledger +import net.corda.testing.node.MockNetwork +import org.junit.Test +import java.util.* +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class IssuerFlowTest { + lateinit var net: MockNetwork + lateinit var notaryNode: MockNetwork.MockNode + lateinit var bankOfCordaNode: MockNetwork.MockNode + lateinit var bankClientNode: MockNetwork.MockNode + + @Test + fun `test issuer flow`() { + net = MockNetwork(false, true) + ledger { + notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) + bankOfCordaNode = net.createPartyNode(notaryNode.info.address, BOC_ISSUER_PARTY.name, BOC_KEY) + bankClientNode = net.createPartyNode(notaryNode.info.address, MEGA_CORP.name, MEGA_CORP_KEY) + + // using default IssueTo Party Reference + val issueToPartyAndRef = MEGA_CORP.ref(OpaqueBytes.of(123)) + val (issuer, issuerResult) = runIssuerAndIssueRequester(1000000.DOLLARS, issueToPartyAndRef) + assertEquals(issuerResult.get(), issuer.get().resultFuture.get()) + + // try to issue an amount of a restricted currency + assertFailsWith { + runIssuerAndIssueRequester(Amount(100000L, currency("BRL")), issueToPartyAndRef).issueRequestResult.get() + } + + bankOfCordaNode.stop() + bankClientNode.stop() + + bankOfCordaNode.manuallyCloseDB() + bankClientNode.manuallyCloseDB() + } + } + + private fun runIssuerAndIssueRequester(amount: Amount, issueToPartyAndRef: PartyAndReference) : RunResult { + val issuerFuture = bankOfCordaNode.initiateSingleShotFlow(IssuerFlow.IssuanceRequester::class) { + otherParty -> IssuerFlow.Issuer(issueToPartyAndRef.party) + }.map { it.fsm } + + val issueRequest = IssuanceRequester(amount, issueToPartyAndRef.party, issueToPartyAndRef.reference, bankOfCordaNode.info.legalIdentity) + val issueRequestResultFuture = bankClientNode.smm.add(issueRequest).resultFuture + + return RunResult(issuerFuture, issueRequestResultFuture) + } + + private data class RunResult( + val issuer: ListenableFuture>, + val issueRequestResult: ListenableFuture + ) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 89b4247424..1f6ee75173 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,4 +17,5 @@ include 'samples:trader-demo' include 'samples:irs-demo' include 'samples:network-visualiser' include 'samples:simm-valuation-demo' -include 'samples:raft-notary-demo' \ No newline at end of file +include 'samples:raft-notary-demo' +include 'samples:bank-of-corda-demo' \ No newline at end of file From b52c52378f45ca4506363f878ee1e7d96081c4b6 Mon Sep 17 00:00:00 2001 From: "rick.parker" Date: Fri, 9 Dec 2016 09:21:59 +0000 Subject: [PATCH 33/37] Move Future completion outside database transaction so that the effects of the flow will be externally visible at Future completion. --- .../node/services/statemachine/FlowStateMachineImpl.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index c929cb6319..3d2cff0cc5 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -86,15 +86,16 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, val result = try { logic.call() } catch (t: Throwable) { - processException(t) + actionOnEnd() commitTransaction() + _resultFuture?.setException(t) throw ExecutionException(t) } // This is to prevent actionOnEnd being called twice if it throws an exception actionOnEnd() - _resultFuture?.set(result) commitTransaction() + _resultFuture?.set(result) return result } @@ -264,8 +265,8 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, // This can get called in actionOnSuspend *after* we commit the database transaction, so optionally open a new one here. databaseTransaction(database) { actionOnEnd() - _resultFuture?.setException(t) } + _resultFuture?.setException(t) } internal fun resume(scheduler: FiberScheduler) { From ae349a88313eef023a59dfd3e2568c5b7130b05d Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 9 Dec 2016 14:30:52 +0000 Subject: [PATCH 34/37] Adds todos that will improve java interop. --- core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt | 4 ++++ core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt | 4 ++++ core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt | 3 +++ node/src/main/kotlin/net/corda/node/driver/Driver.kt | 3 +++ .../net/corda/node/services/messaging/CordaRPCClient.kt | 3 +++ 5 files changed, 17 insertions(+) diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt index d1396f40e3..ce2dbea0b5 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt @@ -61,6 +61,8 @@ inline fun requireThat(body: Requirements.() -> R) = Requirements.body() //// Authenticated commands /////////////////////////////////////////////////////////////////////////////////////////// +// TODO: Provide a version of select that interops with Java + /** Filters the command list by type, party and public key all at once. */ inline fun Collection>.select(signer: CompositeKey? = null, party: Party? = null) = @@ -69,6 +71,8 @@ inline fun Collection filter { if (party == null) true else party in it.signingParties }. map { AuthenticatedObject(it.signers, it.signingParties, it.value as T) } +// TODO: Provide a version of select that interops with Java + /** Filters the command list by type, parties and public keys all at once. */ inline fun Collection>.select(signers: Collection?, parties: Collection?) = diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index 3a9e7186b0..08f619797d 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -54,6 +54,8 @@ abstract class FlowLogic { return sendAndReceive(otherParty, payload, T::class.java) } + // TODO: Move the receiveType param to first position for readability + @Suspendable fun sendAndReceive(otherParty: Party, payload: Any, receiveType: Class): UntrustworthyData { return fsm.sendAndReceive(otherParty, payload, receiveType, sessionFlow) @@ -61,6 +63,8 @@ abstract class FlowLogic { inline fun receive(otherParty: Party): UntrustworthyData = receive(otherParty, T::class.java) + // TODO: Move the receiveType param to first position for readability + @Suspendable fun receive(otherParty: Party, receiveType: Class): UntrustworthyData { return fsm.receive(otherParty, receiveType, sessionFlow) diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 56f9384747..5b5b8ba8a6 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -31,6 +31,9 @@ sealed class StateMachineUpdate(val id: StateMachineRunId) { * RPC operations that the node exposes to clients using the Java client library. These can be called from * client apps and are implemented by the node in the [CordaRPCOpsImpl] class. */ + +// TODO: The use of Pairs throughout is unfriendly for Java interop. + interface CordaRPCOps : RPCOps { /** * Returns a pair of currently in-progress state machine infos and an observable of future state machine adds/removes. diff --git a/node/src/main/kotlin/net/corda/node/driver/Driver.kt b/node/src/main/kotlin/net/corda/node/driver/Driver.kt index 437a5b159c..b98e64f5f3 100644 --- a/node/src/main/kotlin/net/corda/node/driver/Driver.kt +++ b/node/src/main/kotlin/net/corda/node/driver/Driver.kt @@ -132,6 +132,9 @@ sealed class PortAllocation { * @param dsl The dsl itself. * @return The value returned in the [dsl] closure. */ + +// TODO: Add an @JvmOverloads annotation + fun driver( driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()), portAllocation: PortAllocation = PortAllocation.Incremental(10000), diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/CordaRPCClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/CordaRPCClient.kt index b5485a333c..e959406619 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/CordaRPCClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/CordaRPCClient.kt @@ -96,6 +96,9 @@ class CordaRPCClient(val host: HostAndPort, override val config: NodeSSLConfigur * * @throws RPCException if the server version is too low or if the server isn't reachable within the given time. */ + + // TODO: Add an @JvmOverloads annotation + @Throws(RPCException::class) fun proxy(timeout: Duration? = null, minVersion: Int = 0): CordaRPCOps { return state.locked { From ba321121e51d9dc1d582690d6e274a406b047253 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 9 Dec 2016 15:05:18 +0000 Subject: [PATCH 35/37] Fixed instructions in readme --- gradle-plugins/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle-plugins/README.rst b/gradle-plugins/README.rst index 647398b752..cf22310800 100644 --- a/gradle-plugins/README.rst +++ b/gradle-plugins/README.rst @@ -18,7 +18,7 @@ If you need to bootstrap the corda repository you can install these plugins with .. code-block:: text cd publish-utils - ../../gradle install + ../../gradlew -u install cd ../ - ../gradle install + ../gradlew install From 57975c6e91f1bcbac990e54582595b1a9119e52a Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 9 Dec 2016 15:17:07 +0000 Subject: [PATCH 36/37] Bank of corda demo now uses the correct runtime artifacts dependency. --- samples/bank-of-corda-demo/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 89796c27ba..d4b0471a01 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -45,7 +45,7 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' // Corda integration dependencies - runtime project(path: ":node", configuration: 'runtimeArtifacts') + runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') compile project(':core') compile project(':client') compile project(':node') From 9d98673c66c4b11caa201029ed4d076f6476d27e Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 8 Dec 2016 16:33:01 +0000 Subject: [PATCH 37/37] Added commonName extension method to X500Name and helper class for x509 cert factories --- .../net/corda/core/crypto/X509Utilities.kt | 18 +++++----- .../net/corda/core/messaging/Messaging.kt | 2 -- .../corda/core/crypto/X509UtilitiesTest.kt | 4 +-- .../node/services/messaging/RPCDispatcher.kt | 5 ++- .../statemachine/StateMachineManager.kt | 3 +- .../HTTPCertificateSigningService.kt | 35 +++++++++++-------- 6 files changed, 35 insertions(+), 32 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt b/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt index fb9cdb33c9..c29454b74c 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/X509Utilities.kt @@ -430,14 +430,8 @@ object X509Utilities { fun loadCertificateFromPEMFile(filename: Path): X509Certificate { val reader = PemReader(FileReader(filename.toFile())) val pemObject = reader.readPemObject() - val certFact = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME) - val inputStream = ByteArrayInputStream(pemObject.content) - try { - val cert = certFact.generateCertificate(inputStream) as X509Certificate - cert.checkValidity() - return cert - } finally { - inputStream.close() + return CertificateStream(pemObject.content.inputStream()).nextCertificate().apply { + checkValidity() } } @@ -609,3 +603,11 @@ object X509Utilities { return keyStore } } + +val X500Name.commonName: String get() = getRDNs(BCStyle.CN).first().first.value.toString() + +class CertificateStream(val input: InputStream) { + private val certificateFactory = CertificateFactory.getInstance("X.509") + + fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt b/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt index 4d2d628976..0a55d7dca4 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt @@ -8,7 +8,6 @@ import net.corda.core.serialization.DeserializeAsKotlinObjectDef import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.style.BCStyle import java.time.Instant import java.util.* import java.util.concurrent.atomic.AtomicBoolean @@ -184,7 +183,6 @@ interface Message { interface ReceivedMessage : Message { /** The authenticated sender. */ val peer: X500Name - val peerLegalName: String get() = peer.getRDNs(BCStyle.CN).first().first.value.toString() } /** A singleton that's useful for validating topic strings */ diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt index afd80a5259..a1d354316b 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509UtilitiesTest.kt @@ -2,7 +2,6 @@ package net.corda.core.crypto import net.corda.core.div import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x509.GeneralName import org.junit.Rule import org.junit.Test @@ -249,8 +248,7 @@ class X509UtilitiesTest { val peerChain = clientSocket.session.peerCertificates val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal val x500name = X500Name(peerX500Principal.name) - val cn = x500name.getRDNs(BCStyle.CN).first().first.value.toString() - assertEquals("Mega Corp.", cn) + assertEquals("Mega Corp.", x500name.commonName) val output = DataOutputStream(clientSocket.outputStream) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt index af64bd8353..daf96cb592 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCDispatcher.kt @@ -8,6 +8,7 @@ import com.esotericsoftware.kryo.io.Output import com.google.common.annotations.VisibleForTesting import com.google.common.collect.HashMultimap import net.corda.core.ErrorOr +import net.corda.core.crypto.commonName import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCReturnsObservables import net.corda.core.serialization.SerializedBytes @@ -16,14 +17,12 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.debug import net.corda.node.services.RPCUserService import net.corda.node.services.User -import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.node.utilities.AffinityExecutor import org.apache.activemq.artemis.api.core.Message import org.apache.activemq.artemis.api.core.client.ClientConsumer import org.apache.activemq.artemis.api.core.client.ClientMessage import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.style.BCStyle import rx.Notification import rx.Observable import rx.Subscription @@ -168,7 +167,7 @@ abstract class RPCDispatcher(val ops: RPCOps, val userService: RPCUserService, v val rpcUser = userService.getUser(validatedUser) if (rpcUser != null) { return rpcUser - } else if (X500Name(validatedUser).getRDNs(BCStyle.CN).first().first.value.toString() == nodeLegalName) { + } else if (X500Name(validatedUser).commonName == nodeLegalName) { return nodeUser } else { throw IllegalArgumentException("Validated user '$validatedUser' is not an RPC user nor the NODE user") diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt index 260bd19d6c..69769b2799 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt @@ -12,6 +12,7 @@ import kotlinx.support.jdk8.collections.removeIf import net.corda.core.ThreadBox import net.corda.core.abbreviate import net.corda.core.crypto.Party +import net.corda.core.crypto.commonName import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.StateMachineRunId @@ -220,7 +221,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, // isn't required to be unique // TODO For now have the doorman block signups with identical names, and names with characters that // are used in X.500 name textual serialisation - val otherParty = serviceHub.networkMapCache.getNodeByLegalName(message.peerLegalName)?.legalIdentity + val otherParty = serviceHub.networkMapCache.getNodeByLegalName(message.peer.commonName)?.legalIdentity if (otherParty != null) { onSessionInit(sessionMessage, otherParty) } else { diff --git a/node/src/main/kotlin/net/corda/node/utilities/certsigning/HTTPCertificateSigningService.kt b/node/src/main/kotlin/net/corda/node/utilities/certsigning/HTTPCertificateSigningService.kt index 2c5f1ed6f6..b634289cef 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/certsigning/HTTPCertificateSigningService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/certsigning/HTTPCertificateSigningService.kt @@ -1,12 +1,13 @@ package net.corda.node.utilities.certsigning +import net.corda.core.crypto.CertificateStream import org.apache.commons.io.IOUtils import org.bouncycastle.pkcs.PKCS10CertificationRequest import java.io.IOException import java.net.HttpURLConnection +import java.net.HttpURLConnection.* import java.net.URL import java.security.cert.Certificate -import java.security.cert.CertificateFactory import java.util.* import java.util.zip.ZipInputStream @@ -24,18 +25,17 @@ class HTTPCertificateSigningService(val server: URL) : CertificateSigningService conn.requestMethod = "GET" return when (conn.responseCode) { - HttpURLConnection.HTTP_OK -> conn.inputStream.use { - ZipInputStream(it).use { - val certificates = ArrayList() - while (it.nextEntry != null) { - certificates.add(CertificateFactory.getInstance("X.509").generateCertificate(it)) - } - certificates.toTypedArray() + HTTP_OK -> ZipInputStream(conn.inputStream).use { + val certificates = ArrayList() + val stream = CertificateStream(it) + while (it.nextEntry != null) { + certificates.add(stream.nextCertificate()) } + certificates.toTypedArray() } - HttpURLConnection.HTTP_NO_CONTENT -> null - HttpURLConnection.HTTP_UNAUTHORIZED -> throw IOException("Certificate signing request has been rejected, please contact Corda network administrator for more information.") - else -> throw IOException("Unexpected response code ${conn.responseCode} - ${IOUtils.toString(conn.errorStream)}") + HTTP_NO_CONTENT -> null + HTTP_UNAUTHORIZED -> throw IOException("Certificate signing request has been rejected: ${conn.errorMessage}") + else -> throwUnexpectedResponseCode(conn) } } @@ -49,10 +49,15 @@ class HTTPCertificateSigningService(val server: URL) : CertificateSigningService conn.outputStream.write(request.encoded) return when (conn.responseCode) { - HttpURLConnection.HTTP_OK -> IOUtils.toString(conn.inputStream) - HttpURLConnection.HTTP_FORBIDDEN -> throw IOException("Client version $clientVersion is forbidden from accessing permissioning server, please upgrade to newer version.") - else -> throw IOException("Unexpected response code ${conn.responseCode} - ${IOUtils.toString(conn.errorStream)}") + HTTP_OK -> IOUtils.toString(conn.inputStream) + HTTP_FORBIDDEN -> throw IOException("Client version $clientVersion is forbidden from accessing permissioning server, please upgrade to newer version.") + else -> throwUnexpectedResponseCode(conn) } - } + + private fun throwUnexpectedResponseCode(connection: HttpURLConnection): Nothing { + throw IOException("Unexpected response code ${connection.responseCode} - ${connection.errorMessage}") + } + + private val HttpURLConnection.errorMessage: String get() = IOUtils.toString(errorStream) }