Modify the trader demo so it doesn't need a shell script to start it anymore. Now all configuration is in the driver program.

This commit is contained in:
Mike Hearn
2016-05-16 19:06:55 +02:00
parent 8211a7937b
commit e1f899647a
5 changed files with 60 additions and 121 deletions

View File

@ -84,7 +84,7 @@ tasks.withType(JavaExec) {
jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation" jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation"
} }
// Package up the other demo programs. // Package up the demo programs.
task getRateFixDemo(type: CreateStartScripts) { task getRateFixDemo(type: CreateStartScripts) {
mainClassName = "demos.RateFixDemoKt" mainClassName = "demos.RateFixDemoKt"
applicationName = "get-rate-fix" applicationName = "get-rate-fix"
@ -101,6 +101,14 @@ task getIRSDemo(type: CreateStartScripts) {
classpath = jar.outputs.files + project.configurations.runtime classpath = jar.outputs.files + project.configurations.runtime
} }
task getTraderDemo(type: CreateStartScripts) {
mainClassName = "demos.TraderDemoKt"
applicationName = "trader-demo"
defaultJvmOpts = ["-javaagent:${configurations.quasar.singleFile}"]
outputDir = new File(project.buildDir, 'scripts')
classpath = jar.outputs.files + project.configurations.runtime
}
// Force windows script classpath to wildcard path to avoid the 'Command Line Is Too Long' issues // Force windows script classpath to wildcard path to avoid the 'Command Line Is Too Long' issues
// with generated scripts. Include Jolokia .war explicitly as this isn't picked up by wildcard // with generated scripts. Include Jolokia .war explicitly as this isn't picked up by wildcard
tasks.withType(CreateStartScripts) tasks.withType(CreateStartScripts)
@ -138,5 +146,6 @@ jar.dependsOn quasarScan
applicationDistribution.into("bin") { applicationDistribution.into("bin") {
from(getRateFixDemo) from(getRateFixDemo)
from(getIRSDemo) from(getIRSDemo)
from(getTraderDemo)
fileMode = 0755 fileMode = 0755
} }

View File

@ -14,29 +14,27 @@ The demos have only been tested on MacOS X and Ubuntu Linux. If you have success
The demos create node data directories in the root of the project. If something goes wrong with them, blow away the The demos create node data directories in the root of the project. If something goes wrong with them, blow away the
directories and try again. directories and try again.
For Windows users, the contents of the shell scripts are very trivial and can easily be done by hand from a command
window. Essentially, it just runs Gradle to create the startup scripts, and then starts the node with one set of
flags or another. Alternatively you could play with the new Linux syscall support in Windows 10!
Trader demo Trader demo
----------- -----------
Open two terminals, and in the first run::: Open two terminals, and in the first run:::
./scripts/trader-demo.sh buyer gradle installDist && ./build/install/r3prototyping/trader-demo.sh --mode=buyer
It will compile things, if necessary, then create a directory named "buyer" with a bunch of files inside and start It will compile things, if necessary, then create a directory named trader-demo/buyer with a bunch of files inside and
the node. You should see it waiting for a trade to begin. start the node. You should see it waiting for a trade to begin.
In the second terminal, run:: In the second terminal, run::
./scripts/trader-demo.sh seller ./build/install/r3prototyping/trader-demo.sh --mode=seller
You should see some log lines scroll past, and within a few seconds the messages "Purchase complete - we are a You should see some log lines scroll past, and within a few seconds the messages "Purchase complete - we are a
happy customer!" and "Sale completed - we have a happy customer!" should be printed. happy customer!" and "Sale completed - we have a happy customer!" should be printed.
If it doesn't work, jump on the mailing list and let us know. If it doesn't work, jump on the mailing list and let us know.
On Windows, use the same commands, but run the batch file instead of the shell file.
IRS demo IRS demo
-------- --------

View File

@ -94,11 +94,16 @@ class StateMachineManager(val serviceHub: ServiceHub, val executor: AffinityExec
field.get(null) field.get(null)
} }
companion object {
var restoreCheckpointsOnStart = true
}
init { init {
Fiber.setDefaultUncaughtExceptionHandler { fiber, throwable -> Fiber.setDefaultUncaughtExceptionHandler { fiber, throwable ->
(fiber as ProtocolStateMachineImpl<*>).logger.error("Caught exception from protocol", throwable) (fiber as ProtocolStateMachineImpl<*>).logger.error("Caught exception from protocol", throwable)
} }
restoreCheckpoints() if (restoreCheckpointsOnStart)
restoreCheckpoints()
} }
/** Reads the database map and resurrects any serialised state machines. */ /** Reads the database map and resurrects any serialised state machines. */

View File

@ -1,33 +0,0 @@
#!/bin/bash
mode=$1
if [ ! -e ./gradlew ]; then
echo "Run from the root directory please"
exit 1
fi
if [[ "$SKIP_INSTALL" == "" ]]; then
./gradlew installDist
fi
if [[ "$mode" == "buyer" ]]; then
if [ ! -d buyer ]; then
mkdir buyer
echo "myLegalName = Bank A" >buyer/config
fi
build/install/r3prototyping/bin/r3prototyping --dir=buyer --service-fake-trades --network-address=localhost
elif [[ "$mode" == "seller" ]]; then
if [ ! -d seller ]; then
mkdir seller
echo "myLegalName = Bank B" >seller/config
fi
build/install/r3prototyping/bin/r3prototyping --dir=seller --fake-trade-with=localhost --network-address=localhost:31340 --network-map-identity-file=buyer/identity-public --network-map-address=localhost
else
echo "Run like this, one in each tab:"
echo
echo " scripts/trader-demo.sh buyer"
echo " scripts/trader-demo.sh seller"
fi

View File

@ -4,14 +4,15 @@ import co.paralleluniverse.fibers.Suspendable
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import contracts.CommercialPaper import contracts.CommercialPaper
import core.*
import core.contracts.* import core.contracts.*
import core.crypto.Party import core.crypto.Party
import core.crypto.SecureHash import core.crypto.SecureHash
import core.crypto.generateKeyPair import core.crypto.generateKeyPair
import core.days
import core.logElapsedTime
import core.messaging.SingleMessageRecipient import core.messaging.SingleMessageRecipient
import core.messaging.StateMachineManager
import core.node.Node import core.node.Node
import core.node.NodeConfiguration
import core.node.NodeConfigurationFromConfig import core.node.NodeConfigurationFromConfig
import core.node.NodeInfo import core.node.NodeInfo
import core.node.services.NetworkMapService import core.node.services.NetworkMapService
@ -21,6 +22,8 @@ import core.node.services.ServiceType
import core.node.subsystems.ArtemisMessagingService import core.node.subsystems.ArtemisMessagingService
import core.node.subsystems.NodeWalletService import core.node.subsystems.NodeWalletService
import core.protocols.ProtocolLogic import core.protocols.ProtocolLogic
import core.random63BitValue
import core.seconds
import core.serialization.deserialize import core.serialization.deserialize
import core.utilities.ANSIProgressRenderer import core.utilities.ANSIProgressRenderer
import core.utilities.BriefLogFormatter import core.utilities.BriefLogFormatter
@ -43,17 +46,10 @@ import kotlin.test.assertEquals
fun main(args: Array<String>) { fun main(args: Array<String>) {
val parser = OptionParser() val parser = OptionParser()
val networkAddressArg = parser.accepts("network-address").withRequiredArg().required()
val dirArg = parser.accepts("directory").withRequiredArg().defaultsTo("nodedata")
// Some dummy functionality that won't last long ... val modeArg = parser.accepts("mode").withRequiredArg().required()
val myNetworkAddress = parser.accepts("network-address").withRequiredArg().defaultsTo("localhost")
// Mode flags for the first demo. val theirNetworkAddress = parser.accepts("other-network-address").withRequiredArg().defaultsTo("localhost")
val serviceFakeTradesArg = parser.accepts("service-fake-trades")
val fakeTradeWithArg = parser.accepts("fake-trade-with").requiredUnless(serviceFakeTradesArg).withRequiredArg()
val networkMapIdentityFile = parser.accepts("network-map-identity-file").requiredIf(fakeTradeWithArg).withRequiredArg()
val networkMapNetAddr = parser.accepts("network-map-address").requiredIf(networkMapIdentityFile).withRequiredArg()
val options = try { val options = try {
parser.parse(*args) parser.parse(*args)
@ -63,39 +59,46 @@ fun main(args: Array<String>) {
exitProcess(1) exitProcess(1)
} }
// Suppress the Artemis MQ noise, and activate the demo logging. val mode = options.valueOf(modeArg)
BriefLogFormatter.initVerbose("+demo.buyer", "+demo.seller", "-org.apache.activemq")
val dir = Paths.get(options.valueOf(dirArg)) val DIRNAME = "trader-demo"
val configFile = dir.resolve("config") val BUYER = "buyer"
val SELLER = "seller"
if (!Files.exists(dir)) { if (mode !in setOf(BUYER, SELLER)) {
Files.createDirectory(dir) printHelp()
}
val config = loadConfigFile(configFile)
val advertisedServices: Set<ServiceType>
val myNetAddr = HostAndPort.fromString(options.valueOf(networkAddressArg)).withDefaultPort(Node.DEFAULT_PORT)
val listening = options.has(serviceFakeTradesArg)
if (listening && config.myLegalName != "Bank A") {
println("The buyer node must have a legal name of 'Bank A'. Please edit the config file.")
exitProcess(1) exitProcess(1)
} }
val networkMapId = if (options.has(networkMapIdentityFile)) { // Suppress the Artemis MQ noise, and activate the demo logging.
val addr = HostAndPort.fromString(options.valueOf(networkMapNetAddr)).withDefaultPort(Node.DEFAULT_PORT) BriefLogFormatter.initVerbose("+demo.buyer", "+demo.seller", "-org.apache.activemq")
val path = Paths.get(options.valueOf(networkMapIdentityFile))
val dir = Paths.get(DIRNAME, mode)
Files.createDirectories(dir)
val advertisedServices: Set<ServiceType>
val myNetAddr = HostAndPort.fromString(options.valueOf(myNetworkAddress)).withDefaultPort(if (mode == BUYER) Node.DEFAULT_PORT else 31340)
val theirNetAddr = HostAndPort.fromString(options.valueOf(theirNetworkAddress)).withDefaultPort(if (mode == SELLER) Node.DEFAULT_PORT else 31340)
val listening = mode == BUYER
val config = run {
val override = ConfigFactory.parseString("""myLegalName = ${ if (mode == BUYER) "Bank A" else "Bank B" }""")
NodeConfigurationFromConfig(override.withFallback(ConfigFactory.load()))
}
val networkMapId = if (mode == SELLER) {
val path = Paths.get(DIRNAME, BUYER, "identity-public")
val party = Files.readAllBytes(path).deserialize<Party>() val party = Files.readAllBytes(path).deserialize<Party>()
advertisedServices = emptySet() advertisedServices = emptySet()
NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, setOf(NetworkMapService.Type)) NodeInfo(ArtemisMessagingService.makeRecipient(theirNetAddr), party, setOf(NetworkMapService.Type))
} else { } else {
// We must be the network map service // We must be the network map service
advertisedServices = setOf(NetworkMapService.Type, NotaryService.Type) advertisedServices = setOf(NetworkMapService.Type, NotaryService.Type)
null null
} }
// TODO: Remove this once checkpoint resume works.
StateMachineManager.restoreCheckpointsOnStart = false
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, networkMapId, advertisedServices).start() } val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, networkMapId, advertisedServices).start() }
if (listening) { if (listening) {
@ -110,11 +113,6 @@ fun main(args: Array<String>) {
ANSIProgressRenderer.progressTracker = buyer.progressTracker ANSIProgressRenderer.progressTracker = buyer.progressTracker
node.smm.add("demo.buyer", buyer).get() // This thread will halt forever here. node.smm.add("demo.buyer", buyer).get() // This thread will halt forever here.
} else { } else {
if (!options.has(fakeTradeWithArg)) {
println("Need the --fake-trade-with command line argument")
exitProcess(1)
}
// Make sure we have the transaction prospectus attachment loaded into our store. // Make sure we have the transaction prospectus attachment loaded into our store.
if (node.storage.attachments.openAttachment(TraderDemoProtocolSeller.PROSPECTUS_HASH) == null) { if (node.storage.attachments.openAttachment(TraderDemoProtocolSeller.PROSPECTUS_HASH) == null) {
TraderDemoProtocolSeller::class.java.getResourceAsStream("bank-of-london-cp.jar").use { TraderDemoProtocolSeller::class.java.getResourceAsStream("bank-of-london-cp.jar").use {
@ -123,8 +121,7 @@ fun main(args: Array<String>) {
} }
} }
val peerAddr = HostAndPort.fromString(options.valuesOf(fakeTradeWithArg).single()).withDefaultPort(Node.DEFAULT_PORT) val otherSide = ArtemisMessagingService.makeRecipient(theirNetAddr)
val otherSide = ArtemisMessagingService.makeRecipient(peerAddr)
val seller = TraderDemoProtocolSeller(myNetAddr, otherSide) val seller = TraderDemoProtocolSeller(myNetAddr, otherSide)
ANSIProgressRenderer.progressTracker = seller.progressTracker ANSIProgressRenderer.progressTracker = seller.progressTracker
node.smm.add("demo.seller", seller).get() node.smm.add("demo.seller", seller).get()
@ -277,43 +274,6 @@ class TraderDemoProtocolSeller(val myAddress: HostAndPort,
} }
private fun loadConfigFile(configFile: Path): NodeConfiguration {
fun askAdminToEditConfig(configFile: Path?) {
println()
println("This is the first run, so you should edit the config file in $configFile and then start the node again.")
println()
exitProcess(1)
}
val defaultLegalName = "Global MegaCorp, Ltd."
if (!Files.exists(configFile)) {
createDefaultConfigFile(configFile, defaultLegalName)
askAdminToEditConfig(configFile)
}
System.setProperty("config.file", configFile.toAbsolutePath().toString())
val config = NodeConfigurationFromConfig(ConfigFactory.load())
// Make sure admin did actually edit at least the legal name.
if (config.myLegalName == defaultLegalName)
askAdminToEditConfig(configFile)
return config
}
private fun createDefaultConfigFile(configFile: Path?, defaultLegalName: String) {
Files.write(configFile,
"""
# Node configuration: give the buyer node the name 'Bank of Zurich' (no quotes)
# The seller node can be named whatever you like.
myLegalName = $defaultLegalName
""".trimIndent().toByteArray())
}
private fun printHelp() { private fun printHelp() {
println(""" println("Please refer to the documentation in docs/build/index.html to learn how to run the demo.")
Please refer to the documentation in docs/build/index.html to learn how to run the demo.
""".trimIndent())
} }