mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
IRS Demo - split IRS Demo into two separate applications to showcase … (#1638)
* IRS Demo - split IRS Demo into two separate applications to showcase the separation and usage of RPC client
This commit is contained in:
parent
ba75146446
commit
44a7d872d8
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,6 +11,7 @@ tags
|
|||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
local.properties
|
local.properties
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
# General build files
|
# General build files
|
||||||
**/build/*
|
**/build/*
|
||||||
|
8
.idea/compiler.xml
generated
8
.idea/compiler.xml
generated
@ -2,8 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="1.8">
|
<bytecodeTargetLevel target="1.8">
|
||||||
<module name="api-scanner_main" target="1.8" />
|
|
||||||
<module name="api-scanner_test" target="1.8" />
|
|
||||||
<module name="attachment-demo_integrationTest" target="1.8" />
|
<module name="attachment-demo_integrationTest" target="1.8" />
|
||||||
<module name="attachment-demo_main" target="1.8" />
|
<module name="attachment-demo_main" target="1.8" />
|
||||||
<module name="attachment-demo_test" target="1.8" />
|
<module name="attachment-demo_test" target="1.8" />
|
||||||
@ -21,6 +19,7 @@
|
|||||||
<module name="corda-webserver_integrationTest" target="1.8" />
|
<module name="corda-webserver_integrationTest" target="1.8" />
|
||||||
<module name="corda-webserver_main" target="1.8" />
|
<module name="corda-webserver_main" target="1.8" />
|
||||||
<module name="corda-webserver_test" target="1.8" />
|
<module name="corda-webserver_test" target="1.8" />
|
||||||
|
<module name="cordapp_integrationTest" target="1.8" />
|
||||||
<module name="cordapp_main" target="1.8" />
|
<module name="cordapp_main" target="1.8" />
|
||||||
<module name="cordapp_test" target="1.8" />
|
<module name="cordapp_test" target="1.8" />
|
||||||
<module name="cordform-common_main" target="1.8" />
|
<module name="cordform-common_main" target="1.8" />
|
||||||
@ -43,11 +42,8 @@
|
|||||||
<module name="explorer-capsule_test" target="1.6" />
|
<module name="explorer-capsule_test" target="1.6" />
|
||||||
<module name="explorer_main" target="1.8" />
|
<module name="explorer_main" target="1.8" />
|
||||||
<module name="explorer_test" target="1.8" />
|
<module name="explorer_test" target="1.8" />
|
||||||
<module name="finance_integrationTest" target="1.8" />
|
|
||||||
<module name="finance_main" target="1.8" />
|
<module name="finance_main" target="1.8" />
|
||||||
<module name="finance_test" target="1.8" />
|
<module name="finance_test" target="1.8" />
|
||||||
<module name="gradle-plugins-cordform-common_main" target="1.8" />
|
|
||||||
<module name="gradle-plugins-cordform-common_test" target="1.8" />
|
|
||||||
<module name="graphs_main" target="1.8" />
|
<module name="graphs_main" target="1.8" />
|
||||||
<module name="graphs_test" target="1.8" />
|
<module name="graphs_test" target="1.8" />
|
||||||
<module name="irs-demo_integrationTest" target="1.8" />
|
<module name="irs-demo_integrationTest" target="1.8" />
|
||||||
@ -122,6 +118,8 @@
|
|||||||
<module name="verifier_integrationTest" target="1.8" />
|
<module name="verifier_integrationTest" target="1.8" />
|
||||||
<module name="verifier_main" target="1.8" />
|
<module name="verifier_main" target="1.8" />
|
||||||
<module name="verifier_test" target="1.8" />
|
<module name="verifier_test" target="1.8" />
|
||||||
|
<module name="web_main" target="1.8" />
|
||||||
|
<module name="web_test" target="1.8" />
|
||||||
<module name="webcapsule_main" target="1.6" />
|
<module name="webcapsule_main" target="1.6" />
|
||||||
<module name="webcapsule_test" target="1.6" />
|
<module name="webcapsule_test" target="1.6" />
|
||||||
<module name="webserver-webcapsule_main" target="1.6" />
|
<module name="webserver-webcapsule_main" target="1.6" />
|
||||||
|
@ -4,25 +4,28 @@ This demo brings up three nodes: Bank A, Bank B and a node that simultaneously r
|
|||||||
interest rates oracle. The two banks agree on an interest rate swap, and then do regular fixings of the deal as the
|
interest rates oracle. The two banks agree on an interest rate swap, and then do regular fixings of the deal as the
|
||||||
time on a simulated clock passes.
|
time on a simulated clock passes.
|
||||||
|
|
||||||
To run from the command line in Unix:
|
Functionality is split into two parts - CordApp which provides actual distributed ledger backend and Spring Boot
|
||||||
|
webapp which provides REST API and web frontend. Application communicate using Corda RPC protocol.
|
||||||
|
|
||||||
1. Run ``./gradlew samples:irs-demo:deployNodes`` to install configs and a command line tool under
|
To run from the command line in Unix:
|
||||||
|
1. Run ``./gradlew samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
||||||
``samples/irs-demo/build``
|
``samples/irs-demo/build``
|
||||||
2. Run ``./gradlew samples:irs-demo:installDist``
|
2. Run ``./gradlew samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
||||||
3. Move to the ``samples/irs-demo/build`` directory
|
3. Move to the ``samples/irs-demo/`` directory
|
||||||
4. Run ``./nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
4. Run ``./cordapp/build/nodes/runnodes`` to open up three new terminals with the three nodes (you may have to install xterm)
|
||||||
|
5. Run ``./web/build/webapps/runwebapps`` to open three more terminals for associated webserver
|
||||||
|
|
||||||
To run from the command line in Windows:
|
To run from the command line in Windows:
|
||||||
|
|
||||||
1. Run ``gradlew.bat samples:irs-demo:deployNodes`` to install configs and a command line tool under
|
1. Run ``gradlew.bat samples:irs-demo:cordapp:deployNodes`` to install configs and a command line tool under
|
||||||
``samples\irs-demo\build``
|
``samples\irs-demo\build``
|
||||||
2. Run ``gradlew.bat samples:irs-demo:installDist``
|
2. Run ``gradlew.bat samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers
|
||||||
3. Run ``cd samples\irs-demo\build`` to change current working directory
|
3. Run ``cd samples\irs-demo`` to change current working directory
|
||||||
4. Run ``nodes\runnodes`` to open up several 6 terminals, 2 for each node. First terminal is a web-server associated
|
4. Run ``cordapp\build\nodes\runnodes`` to open up several 3 terminals for each nodes
|
||||||
with every node and second one is Corda interactive shell for the node
|
5. Run ``web\build\webapps\webapps`` to open up several 3 terminals for each nodes' webservers
|
||||||
|
|
||||||
This demo also has a web app. To use this, run nodes and then navigate to http://localhost:10007/web/irsdemo and
|
This demo also has a web app. To use this, run nodes and then navigate to http://localhost:10007/ and
|
||||||
http://localhost:10010/web/irsdemo to see each node's view of the ledger.
|
http://localhost:10010/ to see each node's view of the ledger.
|
||||||
|
|
||||||
To use the web app, click the "Create Deal" button, fill in the form, then click the "Submit" button. You can then use
|
To use the web app, click the "Create Deal" button, fill in the form, then click the "Submit" button. You can then use
|
||||||
the time controls at the top left of the home page to run the fixings. Click any individual trade in the blotter to
|
the time controls at the top left of the home page to run the fixings. Click any individual trade in the blotter to
|
||||||
|
@ -1,6 +1,25 @@
|
|||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
springBootVersion = '1.5.7.RELEASE'
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spring Boot plugin adds a numerous hardcoded dependencies in the version much lower then Corda expects
|
||||||
|
// causing the problems in runtime. Those can be changed by manipulating above properties
|
||||||
|
// See https://github.com/spring-gradle-plugins/dependency-management-plugin/blob/master/README.md#changing-the-value-of-a-version-property
|
||||||
|
ext['artemis.version'] = "$artemis_version"
|
||||||
|
ext['hibernate.version'] = "$hibernate_version"
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
apply plugin: 'org.springframework.boot'
|
||||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
apply plugin: 'net.corda.plugins.cordapp'
|
apply plugin: 'net.corda.plugins.cordapp'
|
||||||
@ -23,59 +42,25 @@ sourceSets {
|
|||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestCompile.extendsFrom testCompile
|
||||||
integrationTestRuntime.extendsFrom testRuntime
|
integrationTestRuntime.extendsFrom testRuntime
|
||||||
|
demoArtifacts.extendsFrom testRuntime
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
compile group: 'commons-io', name: 'commons-io', version: '2.5'
|
||||||
|
compile project(path: ":samples:irs-demo:cordapp", configuration: "demoArtifacts")
|
||||||
// The irs demo CorDapp depends upon Cash CorDapp features
|
compile project(":samples:irs-demo:web")
|
||||||
cordapp project(':finance')
|
compile('org.springframework.boot:spring-boot-starter-web') {
|
||||||
|
exclude module: "spring-boot-starter-logging"
|
||||||
// Corda integration dependencies
|
exclude module: "logback-classic"
|
||||||
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
}
|
||||||
cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
|
|
||||||
cordaCompile project(':core')
|
|
||||||
cordaCompile project(':webserver')
|
|
||||||
|
|
||||||
// Javax is required for webapis
|
|
||||||
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
|
||||||
|
|
||||||
// Cordapp dependencies
|
|
||||||
// Specify your cordapp's dependencies below, including dependent cordapps
|
|
||||||
compile "com.squareup.okhttp3:okhttp:$okhttp_version"
|
|
||||||
|
|
||||||
testCompile project(':node-driver')
|
testCompile project(':node-driver')
|
||||||
testCompile "junit:junit:$junit_version"
|
testCompile "junit:junit:$junit_version"
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
bootRepackage {
|
||||||
directory "./build/nodes"
|
enabled = false
|
||||||
node {
|
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
|
||||||
notary = [validating : true]
|
|
||||||
p2pPort 10002
|
|
||||||
rpcPort 10003
|
|
||||||
webPort 10004
|
|
||||||
cordapps = ["$project.group:finance:$corda_release_version"]
|
|
||||||
useTestClock true
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
name "O=Bank A,L=London,C=GB"
|
|
||||||
p2pPort 10005
|
|
||||||
rpcPort 10006
|
|
||||||
webPort 10007
|
|
||||||
cordapps = ["$project.group:finance:$corda_release_version"]
|
|
||||||
useTestClock true
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
name "O=Bank B,L=New York,C=US"
|
|
||||||
p2pPort 10008
|
|
||||||
rpcPort 10009
|
|
||||||
webPort 10010
|
|
||||||
cordapps = ["$project.group:finance:$corda_release_version"]
|
|
||||||
useTestClock true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test, dependsOn: []) {
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
@ -83,41 +68,9 @@ task integrationTest(type: Test, dependsOn: []) {
|
|||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
// This fixes the "line too long" error when running this demo with windows CLI
|
|
||||||
// TODO: Automatically apply to all projects via a plugin
|
|
||||||
tasks.withType(CreateStartScripts).each { task ->
|
|
||||||
task.doLast {
|
|
||||||
String text = task.windowsScript.text
|
|
||||||
// Replaces the per file classpath (which are all jars in "lib") with a wildcard on lib
|
|
||||||
text = text.replaceFirst(/(set CLASSPATH=%APP_HOME%\\lib\\).*/, { "${it[1]}*" })
|
|
||||||
task.windowsScript.write text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
downloadJavadoc = true // defaults to false
|
downloadJavadoc = true // defaults to false
|
||||||
downloadSources = true
|
downloadSources = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
jarAndSources(MavenPublication) {
|
|
||||||
from components.java
|
|
||||||
artifactId 'irsdemo'
|
|
||||||
|
|
||||||
artifact sourceJar
|
|
||||||
artifact javadocJar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
from sourceSets.test.output
|
|
||||||
manifest {
|
|
||||||
attributes(
|
|
||||||
'Automatic-Module-Name': 'net.corda.samples.demos.irs'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
115
samples/irs-demo/cordapp/build.gradle
Normal file
115
samples/irs-demo/cordapp/build.gradle
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
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: 'net.corda.plugins.cordapp'
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName = 'net.corda.irs.IRSDemo'
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
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
|
||||||
|
demoArtifacts.extendsFrom integrationTestRuntime
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// The irs demo CorDapp depends upon Cash CorDapp features
|
||||||
|
cordapp project(':finance')
|
||||||
|
|
||||||
|
// Corda integration dependencies
|
||||||
|
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
||||||
|
cordaCompile project(':core')
|
||||||
|
|
||||||
|
// Cordapp dependencies
|
||||||
|
// Specify your cordapp's dependencies below, including dependent cordapps
|
||||||
|
compile group: 'commons-io', name: 'commons-io', version: '2.5'
|
||||||
|
|
||||||
|
testCompile project(':node-driver')
|
||||||
|
testCompile "junit:junit:$junit_version"
|
||||||
|
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||||
|
|
||||||
|
ext.rpcUsers = [
|
||||||
|
['username' : "user",
|
||||||
|
'password' : "password",
|
||||||
|
'permissions' : [
|
||||||
|
"StartFlow.net.corda.irs.flows.AutoOfferFlow\$Requester",
|
||||||
|
"StartFlow.net.corda.irs.flows.UpdateBusinessDayFlow\$Broadcast",
|
||||||
|
"StartFlow.net.corda.irs.api.NodeInterestRates\$UploadFixesFlow"
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
|
||||||
|
directory "./build/nodes"
|
||||||
|
node {
|
||||||
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
notary = [validating : true]
|
||||||
|
p2pPort 10002
|
||||||
|
rpcPort 10003
|
||||||
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
|
rpcUsers = ext.rpcUsers
|
||||||
|
useTestClock true
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name "O=Bank A,L=London,C=GB"
|
||||||
|
p2pPort 10005
|
||||||
|
rpcPort 10006
|
||||||
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
|
rpcUsers = ext.rpcUsers
|
||||||
|
useTestClock true
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name "O=Bank B,L=New York,C=US"
|
||||||
|
p2pPort 10008
|
||||||
|
rpcPort 10009
|
||||||
|
cordapps = ["net.corda:finance:$corda_release_version"]
|
||||||
|
rpcUsers = ext.rpcUsers
|
||||||
|
useTestClock true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
// This fixes the "line too long" error when running this demo with windows CLI
|
||||||
|
// TODO: Automatically apply to all projects via a plugin
|
||||||
|
tasks.withType(CreateStartScripts).each { task ->
|
||||||
|
task.doLast {
|
||||||
|
String text = task.windowsScript.text
|
||||||
|
// Replaces the per file classpath (which are all jars in "lib") with a wildcard on lib
|
||||||
|
text = text.replaceFirst(/(set CLASSPATH=%APP_HOME%\\lib\\).*/, { "${it[1]}*" })
|
||||||
|
task.windowsScript.write text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
downloadJavadoc = true // defaults to false
|
||||||
|
downloadSources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
from sourceSets.test.output
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
demoArtifacts jar
|
||||||
|
}
|
@ -13,23 +13,25 @@ import net.corda.core.contracts.UniqueIdentifier
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.messaging.vaultTrackBy
|
import net.corda.core.messaging.vaultTrackBy
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.finance.plugin.registerFinanceJSONMappers
|
import net.corda.finance.plugin.registerFinanceJSONMappers
|
||||||
import net.corda.irs.contract.InterestRateSwap
|
import net.corda.irs.contract.InterestRateSwap
|
||||||
import net.corda.irs.utilities.uploadFile
|
import net.corda.irs.web.IrsDemoWebApplication
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
import net.corda.node.services.config.FullNodeConfiguration
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.test.spring.springDriver
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.DUMMY_BANK_A
|
||||||
|
import net.corda.testing.DUMMY_BANK_B
|
||||||
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.IntegrationTestCategory
|
||||||
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.http.HttpApi
|
import net.corda.testing.http.HttpApi
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.net.URL
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
@ -39,31 +41,34 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
val log = loggerFor<IRSDemoTest>()
|
val log = loggerFor<IRSDemoTest>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val rpcUser = User("user", "password", setOf("ALL"))
|
val rpcUsers = listOf(User("user", "password",
|
||||||
private val currentDate: LocalDate = LocalDate.now()
|
setOf("StartFlow.net.corda.irs.flows.AutoOfferFlow\$Requester",
|
||||||
private val futureDate: LocalDate = currentDate.plusMonths(6)
|
"StartFlow.net.corda.irs.flows.UpdateBusinessDayFlow\$Broadcast",
|
||||||
private val maxWaitTime: Duration = 60.seconds
|
"StartFlow.net.corda.irs.api.NodeInterestRates\$UploadFixesFlow")))
|
||||||
|
|
||||||
|
val currentDate: LocalDate = LocalDate.now()
|
||||||
|
val futureDate: LocalDate = currentDate.plusMonths(6)
|
||||||
|
val maxWaitTime: Duration = 60.seconds
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `runs IRS demo`() {
|
fun `runs IRS demo`() {
|
||||||
driver(useTestClock = true, isDebug = true) {
|
springDriver(useTestClock = true, isDebug = true, extraCordappPackagesToScan = listOf("net.corda.irs")) {
|
||||||
val (controller, nodeA, nodeB) = listOf(
|
val (controller, nodeA, nodeB) = listOf(
|
||||||
startNotaryNode(DUMMY_NOTARY.name, validating = false),
|
startNotaryNode(DUMMY_NOTARY.name, validating = true, rpcUsers = rpcUsers),
|
||||||
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)),
|
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = rpcUsers),
|
||||||
startNode(providedName = DUMMY_BANK_B.name))
|
startNode(providedName = DUMMY_BANK_B.name, rpcUsers = rpcUsers)).map { it.getOrThrow() }
|
||||||
.map { it.getOrThrow() }
|
|
||||||
|
|
||||||
log.info("All nodes started")
|
log.info("All nodes started")
|
||||||
|
|
||||||
val (controllerAddr, nodeAAddr, nodeBAddr) = listOf(
|
val controllerAddrFuture = startSpringBootWebapp(IrsDemoWebApplication::class.java, controller, "/api/irs/demodate")
|
||||||
startWebserver(controller),
|
val nodeAAddrFuture = startSpringBootWebapp(IrsDemoWebApplication::class.java, nodeA, "/api/irs/demodate")
|
||||||
startWebserver(nodeA),
|
val nodeBAddrFuture = startSpringBootWebapp(IrsDemoWebApplication::class.java, nodeB, "/api/irs/demodate")
|
||||||
startWebserver(nodeB))
|
val (controllerAddr, nodeAAddr, nodeBAddr) =
|
||||||
.map { it.getOrThrow().listenAddress }
|
listOf(controllerAddrFuture, nodeAAddrFuture, nodeBAddrFuture).map { it.getOrThrow().listenAddress }
|
||||||
|
|
||||||
log.info("All webservers started")
|
log.info("All webservers started")
|
||||||
|
|
||||||
val (_, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map {
|
val (controllerApi, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map {
|
||||||
val mapper = net.corda.client.jackson.JacksonSupport.createDefaultMapper(it.first.rpc)
|
val mapper = net.corda.client.jackson.JacksonSupport.createDefaultMapper(it.first.rpc)
|
||||||
registerFinanceJSONMappers(mapper)
|
registerFinanceJSONMappers(mapper)
|
||||||
registerIRSModule(mapper)
|
registerIRSModule(mapper)
|
||||||
@ -73,7 +78,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
val numADeals = getTradeCount(nodeAApi)
|
val numADeals = getTradeCount(nodeAApi)
|
||||||
val numBDeals = getTradeCount(nodeBApi)
|
val numBDeals = getTradeCount(nodeBApi)
|
||||||
|
|
||||||
runUploadRates(controllerAddr)
|
runUploadRates(controllerApi)
|
||||||
runTrade(nodeAApi, controller.nodeInfo.chooseIdentity())
|
runTrade(nodeAApi, controller.nodeInfo.chooseIdentity())
|
||||||
|
|
||||||
assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1)
|
assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1)
|
||||||
@ -89,9 +94,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFloatingLegFixCount(nodeApi: HttpApi): Int {
|
fun getFloatingLegFixCount(nodeApi: HttpApi) = getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null }
|
||||||
return getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getFixingDateObservable(config: FullNodeConfiguration): Observable<LocalDate?> {
|
private fun getFixingDateObservable(config: FullNodeConfiguration): Observable<LocalDate?> {
|
||||||
val client = CordaRPCClient(config.rpcAddress!!)
|
val client = CordaRPCClient(config.rpcAddress!!)
|
||||||
@ -111,16 +114,15 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
|
|
||||||
private fun runTrade(nodeApi: HttpApi, oracle: Party) {
|
private fun runTrade(nodeApi: HttpApi, oracle: Party) {
|
||||||
log.info("Running trade against ${nodeApi.root}")
|
log.info("Running trade against ${nodeApi.root}")
|
||||||
val fileContents = loadResourceFile("net/corda/irs/simulation/example-irs-trade.json")
|
val fileContents = loadResourceFile("net/corda/irs/web/simulation/example-irs-trade.json")
|
||||||
val tradeFile = fileContents.replace("tradeXXX", "trade1").replace("oracleXXX", oracle.name.toString())
|
val tradeFile = fileContents.replace("tradeXXX", "trade1").replace("oracleXXX", oracle.name.toString())
|
||||||
assertThat(nodeApi.postJson("deals", tradeFile)).isTrue()
|
assertThat(nodeApi.postJson("deals", tradeFile)).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runUploadRates(host: NetworkHostAndPort) {
|
private fun runUploadRates(nodeApi: HttpApi) {
|
||||||
log.info("Running upload rates against $host")
|
log.info("Running upload rates against ${nodeApi.root}")
|
||||||
val fileContents = loadResourceFile("net/corda/irs/simulation/example.rates.txt")
|
val fileContents = loadResourceFile("net/corda/irs/simulation/example.rates.txt")
|
||||||
val url = URL("http://$host/api/irs/fixes")
|
assertThat(nodeApi.postPlain("fixes", fileContents)).isTrue()
|
||||||
assertThat(uploadFile(url, fileContents)).isTrue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadResourceFile(filename: String): String {
|
private fun loadResourceFile(filename: String): String {
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
package net.corda.test.spring
|
||||||
|
|
||||||
|
import net.corda.core.concurrent.CordaFuture
|
||||||
|
import net.corda.core.internal.concurrent.flatMap
|
||||||
|
import net.corda.core.internal.concurrent.fork
|
||||||
|
import net.corda.core.internal.concurrent.map
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.testing.driver.*
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import java.net.ConnectException
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
interface SpringDriverExposedDSLInterface : DriverDSLExposedInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a Spring Boot application, passes the RPC connection data as parameters the process.
|
||||||
|
* Returns future which will complete after (and if) the server passes healthcheck.
|
||||||
|
* @param clazz Class with main method which is expected to run Spring application
|
||||||
|
* @param handle Corda Node handle this webapp is expected to connect to
|
||||||
|
* @param checkUrl URL path to use for server readiness check - uses [okhttp3.Response.isSuccessful] as qualifier
|
||||||
|
*
|
||||||
|
* TODO: Rather then expecting a given clazz to contain main method which start Spring app our own simple class can do this
|
||||||
|
*/
|
||||||
|
fun startSpringBootWebapp(clazz: Class<*>, handle: NodeHandle, checkUrl: String): CordaFuture<WebserverHandle>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpringDriverInternalDSLInterface : DriverDSLInternalInterface, SpringDriverExposedDSLInterface
|
||||||
|
|
||||||
|
fun <A> springDriver(
|
||||||
|
defaultParameters: DriverParameters = DriverParameters(),
|
||||||
|
isDebug: Boolean = defaultParameters.isDebug,
|
||||||
|
driverDirectory: Path = defaultParameters.driverDirectory,
|
||||||
|
portAllocation: PortAllocation = defaultParameters.portAllocation,
|
||||||
|
debugPortAllocation: PortAllocation = defaultParameters.debugPortAllocation,
|
||||||
|
systemProperties: Map<String, String> = defaultParameters.systemProperties,
|
||||||
|
useTestClock: Boolean = defaultParameters.useTestClock,
|
||||||
|
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
|
||||||
|
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
||||||
|
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||||
|
dsl: SpringDriverExposedDSLInterface.() -> A
|
||||||
|
) = genericDriver(
|
||||||
|
defaultParameters = defaultParameters,
|
||||||
|
isDebug = isDebug,
|
||||||
|
driverDirectory = driverDirectory,
|
||||||
|
portAllocation = portAllocation,
|
||||||
|
debugPortAllocation = debugPortAllocation,
|
||||||
|
systemProperties = systemProperties,
|
||||||
|
useTestClock = useTestClock,
|
||||||
|
initialiseSerialization = initialiseSerialization,
|
||||||
|
startNodesInProcess = startNodesInProcess,
|
||||||
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
|
driverDslWrapper = { driverDSL:DriverDSL -> SpringBootDriverDSL(driverDSL) },
|
||||||
|
coerce = { it },
|
||||||
|
dsl = dsl
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SpringBootDriverDSL(
|
||||||
|
val driverDSL: DriverDSL
|
||||||
|
) : DriverDSLInternalInterface by driverDSL, SpringDriverInternalDSLInterface {
|
||||||
|
|
||||||
|
val log = loggerFor<SpringBootDriverDSL>()
|
||||||
|
|
||||||
|
override fun startSpringBootWebapp(clazz: Class<*>, handle: NodeHandle, checkUrl: String): CordaFuture<WebserverHandle> {
|
||||||
|
val debugPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null
|
||||||
|
val processFuture = startApplication(driverDSL.executorService, handle, debugPort, clazz)
|
||||||
|
driverDSL.registerProcess(processFuture)
|
||||||
|
return processFuture.map { queryWebserver(handle, it, checkUrl) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryWebserver(handle: NodeHandle, process: Process, checkUrl: String): WebserverHandle {
|
||||||
|
val protocol = if (handle.configuration.useHTTPS) "https://" else "http://"
|
||||||
|
val url = URL(URL("$protocol${handle.webAddress}"), checkUrl)
|
||||||
|
val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build()
|
||||||
|
|
||||||
|
var maxRetries = 30
|
||||||
|
|
||||||
|
while (process.isAlive && maxRetries > 0) try {
|
||||||
|
val response = client.newCall(Request.Builder().url(url).build()).execute()
|
||||||
|
response.use {
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
return WebserverHandle(handle.webAddress, process)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeUnit.SECONDS.sleep(2)
|
||||||
|
maxRetries--
|
||||||
|
} catch (e: ConnectException) {
|
||||||
|
log.debug("Retrying webserver info at ${handle.webAddress}")
|
||||||
|
}
|
||||||
|
|
||||||
|
throw IllegalStateException("Webserver at ${handle.webAddress} has died or was not reachable at URL ${url}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startApplication(executorService: ExecutorService, handle: NodeHandle, debugPort: Int?, clazz: Class<*>): CordaFuture<Process> {
|
||||||
|
return executorService.fork {
|
||||||
|
val className = clazz.canonicalName
|
||||||
|
ProcessUtilities.startJavaProcessImpl(
|
||||||
|
className = className, // cannot directly get class for this, so just use string
|
||||||
|
jdwpPort = debugPort,
|
||||||
|
extraJvmArguments = listOf(
|
||||||
|
"-Dname=node-${handle.configuration.p2pAddress}-webserver",
|
||||||
|
"-Djava.io.tmpdir=${System.getProperty("java.io.tmpdir")}"
|
||||||
|
// Inherit from parent process
|
||||||
|
),
|
||||||
|
classpath = ProcessUtilities.defaultClassPath,
|
||||||
|
workingDirectory = handle.configuration.baseDirectory,
|
||||||
|
errorLogPath = Paths.get("error.$className.log"),
|
||||||
|
arguments = listOf(
|
||||||
|
"--base-directory", handle.configuration.baseDirectory.toString(),
|
||||||
|
"--server.port=${handle.webAddress.port}",
|
||||||
|
"--corda.host=${handle.configuration.rpcAddress}",
|
||||||
|
"--corda.user=${handle.configuration.rpcUsers.first().username}",
|
||||||
|
"--corda.password=${handle.configuration.rpcUsers.first().password}"
|
||||||
|
),
|
||||||
|
maximumHeapSize = null
|
||||||
|
)
|
||||||
|
}.flatMap { process -> addressMustBeBoundFuture(driverDSL.executorService, handle.webAddress, process).map { process } }
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
package net.corda.irs.plugin
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import net.corda.finance.plugin.registerFinanceJSONMappers
|
|
||||||
import net.corda.irs.api.InterestRateSwapAPI
|
|
||||||
import net.corda.webserver.services.WebServerPluginRegistry
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
class IRSPlugin : WebServerPluginRegistry {
|
|
||||||
override val webApis = listOf(Function(::InterestRateSwapAPI))
|
|
||||||
override val staticServeDirs: Map<String, String> = mapOf(
|
|
||||||
"irsdemo" to javaClass.classLoader.getResource("irsweb").toExternalForm()
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om)
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package net.corda.irs.utilities
|
|
||||||
|
|
||||||
import okhttp3.MediaType
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.RequestBody
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A small set of utilities for making HttpCalls, aimed at demos.
|
|
||||||
*/
|
|
||||||
private val client by lazy {
|
|
||||||
OkHttpClient.Builder()
|
|
||||||
.connectTimeout(5, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(60, TimeUnit.SECONDS).build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun putJson(url: URL, data: String): Boolean {
|
|
||||||
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), data)
|
|
||||||
return makeRequest(Request.Builder().url(url).put(body).build())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun postJson(url: URL, data: String): Boolean {
|
|
||||||
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), data)
|
|
||||||
return makeRequest(Request.Builder().url(url).post(body).build())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uploadFile(url: URL, file: String): Boolean {
|
|
||||||
val body = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), file)
|
|
||||||
return makeRequest(Request.Builder().url(url).post(body).build())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeRequest(request: Request): Boolean {
|
|
||||||
val response = client.newCall(request).execute()
|
|
||||||
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
println("Could not fulfill HTTP request. Status Code: ${response.code()}. Message: ${response.body().string()}")
|
|
||||||
}
|
|
||||||
response.close()
|
|
||||||
return response.isSuccessful
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
# Register a ServiceLoader service extending from net.corda.webserver.services.WebServerPluginRegistry
|
|
||||||
net.corda.irs.plugin.IRSPlugin
|
|
@ -1,9 +0,0 @@
|
|||||||
package net.corda.irs.plugin
|
|
||||||
|
|
||||||
import net.corda.irs.api.InterestRatesSwapDemoAPI
|
|
||||||
import net.corda.webserver.services.WebServerPluginRegistry
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
class IRSDemoPlugin : WebServerPluginRegistry {
|
|
||||||
override val webApis = listOf(Function(::InterestRatesSwapDemoAPI))
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
net.corda.irs.plugin.IRSPlugin
|
|
||||||
net.corda.irs.plugin.IRSDemoPlugin
|
|
55
samples/irs-demo/web/build.gradle
Normal file
55
samples/irs-demo/web/build.gradle
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
|
||||||
|
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlin_version}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spring Boot plugin adds a numerous hardcoded dependencies in the version much lower then Corda expects
|
||||||
|
// causing the problems in runtime. Those can be changed by manipulating above properties
|
||||||
|
// See https://github.com/spring-gradle-plugins/dependency-management-plugin/blob/master/README.md#changing-the-value-of-a-version-property
|
||||||
|
ext['artemis.version'] = "$artemis_version"
|
||||||
|
ext['hibernate.version'] = "$hibernate_version"
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'kotlin-spring'
|
||||||
|
apply plugin: 'eclipse'
|
||||||
|
apply plugin: 'org.springframework.boot'
|
||||||
|
apply plugin: 'project-report'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile('org.springframework.boot:spring-boot-starter-web') {
|
||||||
|
exclude module: "spring-boot-starter-logging"
|
||||||
|
exclude module: "logback-classic"
|
||||||
|
}
|
||||||
|
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.8.9")
|
||||||
|
compile project(":client:rpc")
|
||||||
|
compile project(":client:jackson")
|
||||||
|
compile project(":test-utils")
|
||||||
|
compile project(path: ":samples:irs-demo:cordapp", configuration: "demoArtifacts")
|
||||||
|
testCompile('org.springframework.boot:spring-boot-starter-test') {
|
||||||
|
exclude module: "spring-boot-starter-logging"
|
||||||
|
exclude module: "logback-classic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
from sourceSets.test.output
|
||||||
|
}
|
||||||
|
|
||||||
|
task deployWebapps(type: Copy, dependsOn: ['jar', 'bootRepackage']) {
|
||||||
|
ext.webappDir = file("build/webapps")
|
||||||
|
|
||||||
|
from(jar.outputs)
|
||||||
|
from("src/test/resources/scripts/") {
|
||||||
|
filter { it
|
||||||
|
.replace('#JAR_PATH#', jar.archiveName)
|
||||||
|
.replace('#DIR#', ext.webappDir.getAbsolutePath())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into ext.webappDir
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package net.corda.irs.web
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import net.corda.client.jackson.JacksonSupport
|
||||||
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.finance.plugin.registerFinanceJSONMappers
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.boot.SpringApplication
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple and sample SpringBoot web application which communicates with Corda node using RPC.
|
||||||
|
* [CordaRPCOps] instance can be managed simply as plain Spring bean.
|
||||||
|
* If support for (de)serializatin of Corda classes is required, [ObjectMapper] can be configured using helper
|
||||||
|
* functions, see [objectMapper]
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
class IrsDemoWebApplication {
|
||||||
|
@Value("\${corda.host}")
|
||||||
|
lateinit var cordaHost:String
|
||||||
|
|
||||||
|
@Value("\${corda.user}")
|
||||||
|
lateinit var cordaUser:String
|
||||||
|
|
||||||
|
@Value("\${corda.password}")
|
||||||
|
lateinit var cordaPassword:String
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun rpcClient(): CordaRPCOps {
|
||||||
|
return CordaRPCClient(NetworkHostAndPort.parse(cordaHost)).start(cordaUser, cordaPassword).proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun objectMapper(@Autowired cordaRPCOps: CordaRPCOps): ObjectMapper {
|
||||||
|
val mapper = JacksonSupport.createDefaultMapper(cordaRPCOps)
|
||||||
|
registerFinanceJSONMappers(mapper)
|
||||||
|
return mapper
|
||||||
|
}
|
||||||
|
|
||||||
|
// running as standalone java app
|
||||||
|
companion object {
|
||||||
|
@JvmStatic fun main(args: Array<String>) {
|
||||||
|
SpringApplication.run(IrsDemoWebApplication::class.java, *args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.irs.api
|
package net.corda.irs.web.api
|
||||||
|
|
||||||
import net.corda.core.contracts.filterStatesOfType
|
import net.corda.core.contracts.filterStatesOfType
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
@ -7,11 +7,12 @@ import net.corda.core.messaging.vaultQueryBy
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.irs.contract.InterestRateSwap
|
import net.corda.irs.contract.InterestRateSwap
|
||||||
import net.corda.irs.flows.AutoOfferFlow
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.web.bind.annotation.*
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import javax.ws.rs.*
|
import net.corda.irs.flows.AutoOfferFlow
|
||||||
import javax.ws.rs.core.MediaType
|
|
||||||
import javax.ws.rs.core.Response
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides a simplified API, currently for demonstration use only.
|
* This provides a simplified API, currently for demonstration use only.
|
||||||
@ -28,8 +29,10 @@ import javax.ws.rs.core.Response
|
|||||||
*
|
*
|
||||||
* TODO: replace simulated date advancement with business event based implementation
|
* TODO: replace simulated date advancement with business event based implementation
|
||||||
*/
|
*/
|
||||||
@Path("irs")
|
|
||||||
class InterestRateSwapAPI(val rpc: CordaRPCOps) {
|
@RestController
|
||||||
|
@RequestMapping("/api/irs")
|
||||||
|
class InterestRateSwapAPI {
|
||||||
|
|
||||||
private val logger = loggerFor<InterestRateSwapAPI>()
|
private val logger = loggerFor<InterestRateSwapAPI>()
|
||||||
|
|
||||||
@ -44,6 +47,10 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var rpc: CordaRPCOps
|
||||||
|
|
||||||
private fun getAllDeals(): Array<InterestRateSwap.State> {
|
private fun getAllDeals(): Array<InterestRateSwap.State> {
|
||||||
val vault = rpc.vaultQueryBy<InterestRateSwap.State>().states
|
val vault = rpc.vaultQueryBy<InterestRateSwap.State>().states
|
||||||
val states = vault.filterStatesOfType<InterestRateSwap.State>()
|
val states = vault.filterStatesOfType<InterestRateSwap.State>()
|
||||||
@ -51,33 +58,30 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
|
|||||||
return swaps
|
return swaps
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GetMapping("/deals")
|
||||||
@Path("deals")
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
fun fetchDeals(): Array<InterestRateSwap.State> = getAllDeals()
|
fun fetchDeals(): Array<InterestRateSwap.State> = getAllDeals()
|
||||||
|
|
||||||
@POST
|
@PostMapping("/deals")
|
||||||
@Path("deals")
|
fun storeDeal(@RequestBody newDeal: InterestRateSwap.State): ResponseEntity<Any?> {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
fun storeDeal(newDeal: InterestRateSwap.State): Response {
|
|
||||||
return try {
|
return try {
|
||||||
rpc.startFlow(AutoOfferFlow::Requester, newDeal).returnValue.getOrThrow()
|
rpc.startFlow(AutoOfferFlow::Requester, newDeal).returnValue.getOrThrow()
|
||||||
Response.created(URI.create(generateDealLink(newDeal))).build()
|
ResponseEntity.created(URI.create(generateDealLink(newDeal))).build()
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
logger.info("Exception when creating deal: $ex")
|
logger.info("Exception when creating deal: $ex", ex)
|
||||||
Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.toString()).build()
|
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GetMapping("/deals/{ref:.+}")
|
||||||
@Path("deals/{ref}")
|
fun fetchDeal(@PathVariable ref: String?): ResponseEntity<Any?> {
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
val deal = getDealByRef(ref!!)
|
||||||
fun fetchDeal(@PathParam("ref") ref: String): Response {
|
return if (deal == null) {
|
||||||
val deal = getDealByRef(ref)
|
ResponseEntity.notFound().build()
|
||||||
if (deal == null) {
|
|
||||||
return Response.status(Response.Status.NOT_FOUND).build()
|
|
||||||
} else {
|
} else {
|
||||||
return Response.ok().entity(deal).build()
|
ResponseEntity.ok(deal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/deals/networksnapshot")
|
||||||
|
fun fetchDeal() = rpc.networkMapSnapshot().toString()
|
||||||
}
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
corda.host=localhost:10006
|
||||||
|
server.port=10007
|
@ -0,0 +1,2 @@
|
|||||||
|
corda.host=localhost:10009
|
||||||
|
server.port=10010
|
@ -0,0 +1,2 @@
|
|||||||
|
corda.host=localhost:10003
|
||||||
|
server.port=10004
|
@ -0,0 +1,2 @@
|
|||||||
|
corda.user=user
|
||||||
|
corda.password=password
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user