Merged in simplify-demo-execution (pull request #99)

Simplify demo execution
This commit is contained in:
Mike Hearn 2016-05-17 18:14:45 +02:00
commit 5b1b5773ee
5 changed files with 60 additions and 121 deletions

View File

@ -84,7 +84,7 @@ tasks.withType(JavaExec) {
jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation"
}
// Package up the other demo programs.
// Package up the demo programs.
task getRateFixDemo(type: CreateStartScripts) {
mainClassName = "demos.RateFixDemoKt"
applicationName = "get-rate-fix"
@ -101,6 +101,14 @@ task getIRSDemo(type: CreateStartScripts) {
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
// with generated scripts. Include Jolokia .war explicitly as this isn't picked up by wildcard
tasks.withType(CreateStartScripts)
@ -138,5 +146,6 @@ jar.dependsOn quasarScan
applicationDistribution.into("bin") {
from(getRateFixDemo)
from(getIRSDemo)
from(getTraderDemo)
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
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
-----------
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
the node. You should see it waiting for a trade to begin.
It will compile things, if necessary, then create a directory named trader-demo/buyer with a bunch of files inside and
start the node. You should see it waiting for a trade to begin.
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
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.
On Windows, use the same commands, but run the batch file instead of the shell file.
IRS demo
--------

View File

@ -94,11 +94,16 @@ class StateMachineManager(val serviceHub: ServiceHub, val executor: AffinityExec
field.get(null)
}
companion object {
var restoreCheckpointsOnStart = true
}
init {
Fiber.setDefaultUncaughtExceptionHandler { fiber, 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. */

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.typesafe.config.ConfigFactory
import contracts.CommercialPaper
import core.*
import core.contracts.*
import core.crypto.Party
import core.crypto.SecureHash
import core.crypto.generateKeyPair
import core.days
import core.logElapsedTime
import core.messaging.SingleMessageRecipient
import core.messaging.StateMachineManager
import core.node.Node
import core.node.NodeConfiguration
import core.node.NodeConfigurationFromConfig
import core.node.NodeInfo
import core.node.services.NetworkMapService
@ -21,6 +22,8 @@ import core.node.services.ServiceType
import core.node.subsystems.ArtemisMessagingService
import core.node.subsystems.NodeWalletService
import core.protocols.ProtocolLogic
import core.random63BitValue
import core.seconds
import core.serialization.deserialize
import core.utilities.ANSIProgressRenderer
import core.utilities.BriefLogFormatter
@ -43,17 +46,10 @@ import kotlin.test.assertEquals
fun main(args: Array<String>) {
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 ...
// Mode flags for the first demo.
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 modeArg = parser.accepts("mode").withRequiredArg().required()
val myNetworkAddress = parser.accepts("network-address").withRequiredArg().defaultsTo("localhost")
val theirNetworkAddress = parser.accepts("other-network-address").withRequiredArg().defaultsTo("localhost")
val options = try {
parser.parse(*args)
@ -63,39 +59,46 @@ fun main(args: Array<String>) {
exitProcess(1)
}
// Suppress the Artemis MQ noise, and activate the demo logging.
BriefLogFormatter.initVerbose("+demo.buyer", "+demo.seller", "-org.apache.activemq")
val mode = options.valueOf(modeArg)
val dir = Paths.get(options.valueOf(dirArg))
val configFile = dir.resolve("config")
val DIRNAME = "trader-demo"
val BUYER = "buyer"
val SELLER = "seller"
if (!Files.exists(dir)) {
Files.createDirectory(dir)
}
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.")
if (mode !in setOf(BUYER, SELLER)) {
printHelp()
exitProcess(1)
}
val networkMapId = if (options.has(networkMapIdentityFile)) {
val addr = HostAndPort.fromString(options.valueOf(networkMapNetAddr)).withDefaultPort(Node.DEFAULT_PORT)
val path = Paths.get(options.valueOf(networkMapIdentityFile))
// Suppress the Artemis MQ noise, and activate the demo logging.
BriefLogFormatter.initVerbose("+demo.buyer", "+demo.seller", "-org.apache.activemq")
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>()
advertisedServices = emptySet()
NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, setOf(NetworkMapService.Type))
NodeInfo(ArtemisMessagingService.makeRecipient(theirNetAddr), party, setOf(NetworkMapService.Type))
} else {
// We must be the network map service
advertisedServices = setOf(NetworkMapService.Type, NotaryService.Type)
null
}
// TODO: Remove this once checkpoint resume works.
StateMachineManager.restoreCheckpointsOnStart = false
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, networkMapId, advertisedServices).start() }
if (listening) {
@ -110,11 +113,6 @@ fun main(args: Array<String>) {
ANSIProgressRenderer.progressTracker = buyer.progressTracker
node.smm.add("demo.buyer", buyer).get() // This thread will halt forever here.
} 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.
if (node.storage.attachments.openAttachment(TraderDemoProtocolSeller.PROSPECTUS_HASH) == null) {
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(peerAddr)
val otherSide = ArtemisMessagingService.makeRecipient(theirNetAddr)
val seller = TraderDemoProtocolSeller(myNetAddr, otherSide)
ANSIProgressRenderer.progressTracker = seller.progressTracker
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() {
println("""
Please refer to the documentation in docs/build/index.html to learn how to run the demo.
""".trimIndent())
println("Please refer to the documentation in docs/build/index.html to learn how to run the demo.")
}