diff --git a/tools/demobench/build.gradle b/tools/demobench/build.gradle index a9fd82175b..58ae835a85 100644 --- a/tools/demobench/build.gradle +++ b/tools/demobench/build.gradle @@ -9,6 +9,7 @@ buildscript { ext.guava_version = '14.0.1' ext.slf4j_version = '1.7.22' ext.logback_version = '1.1.3' + ext.typesafeconfig_version = '1.3.1' repositories { mavenCentral() @@ -50,6 +51,7 @@ dependencies { compile "org.slf4j:log4j-over-slf4j:$slf4j_version" compile "org.slf4j:jul-to-slf4j:$slf4j_version" compile "ch.qos.logback:logback-classic:$logback_version" + compile "com.typesafe:config:$typesafeconfig_version" compile ":jediterm-terminal-2.5" compile ':pty4j-0.7.2' diff --git a/tools/demobench/libs/jediterm-terminal-2.5.jar b/tools/demobench/libs/jediterm-terminal-2.5.jar index 66937731a2..b211b2d08e 100644 Binary files a/tools/demobench/libs/jediterm-terminal-2.5.jar and b/tools/demobench/libs/jediterm-terminal-2.5.jar differ diff --git a/tools/demobench/src/main/java/net/corda/demobench/pty/R3Pty.java b/tools/demobench/src/main/java/net/corda/demobench/pty/R3Pty.java index 5a0ec1213e..0f79d7557a 100644 --- a/tools/demobench/src/main/java/net/corda/demobench/pty/R3Pty.java +++ b/tools/demobench/src/main/java/net/corda/demobench/pty/R3Pty.java @@ -28,9 +28,7 @@ public class R3Pty implements AutoCloseable { @Override public void close() { LOG.info("Closing terminal '{}'", name); - if (terminal.getTerminalStarter() != null) { - terminal.close(); - } + terminal.close(); } public String getName() { @@ -63,7 +61,9 @@ public class R3Pty implements AutoCloseable { } Map environment = new HashMap<>(envs); - environment.put("TERM", "xterm"); + if (!UIUtil.isWindows) { + environment.put("TERM", "xterm"); + } TerminalSession session = terminal.createTerminalSession(createTtyConnector(args, environment, workingDir)); session.start(); diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt index c5798abe9d..f25700b2fa 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt @@ -1,14 +1,23 @@ package net.corda.demobench.model -class NodeConfig(name: String, p2pPort: Int, artemisPort: Int, webPort: Int) { +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import com.typesafe.config.ConfigValue +import com.typesafe.config.ConfigValueFactory - private var keyValue: String = toKey(name) +class NodeConfig(legalName: String, nearestCity: String, p2pPort: Int, artemisPort: Int, webPort: Int) { + + private var keyValue: String = toKey(legalName) val key : String get() { return keyValue } - private var nameValue: String = name - val name : String - get() { return nameValue } + private var legalNameValue: String = legalName + val legalName : String + get() { return legalNameValue } + + private var nearestCityName: String = nearestCity + val nearestCity : String + get() { return nearestCityName } private var p2pPortValue: Int = p2pPort val p2pPort : Int @@ -26,4 +35,27 @@ class NodeConfig(name: String, p2pPort: Int, artemisPort: Int, webPort: Int) { return value.replace("\\s++", "").toLowerCase() } + val toFileConfig : Config + get() = ConfigFactory.empty() + .withValue("myLegalName", valueFor(legalName)) + .withValue("nearestCity", valueFor(nearestCity)) + .withValue("extraAdvertisedServiceIds", valueFor("")) + .withFallback(ConfigFactory.empty() + .withValue("address", addressValueFor(p2pPort)) + .withValue("legalName", valueFor("Notary")) + .atPath("networkMapService") + ) + .withValue("artemisAddress", addressValueFor(artemisPort)) + .withValue("webAddress", addressValueFor(webPort)) + .withValue("rpcUsers", valueFor(listOf())) + .withValue("useTestClock", valueFor(true)) + +} + +private fun valueFor(any: T): ConfigValue? { + return ConfigValueFactory.fromAnyRef(any) +} + +private fun addressValueFor(port: Int): ConfigValue? { + return valueFor("localhost:%d".format(port)) } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 025816eabc..70fdf0464b 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -1,18 +1,35 @@ package net.corda.demobench.model -import tornadofx.Controller +import com.jediterm.terminal.ui.UIUtil +import com.typesafe.config.ConfigRenderOptions +import java.lang.management.ManagementFactory +import java.nio.file.Paths +import java.text.SimpleDateFormat +import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger +import net.corda.demobench.pty.R3Pty +import tornadofx.Controller class NodeController : Controller() { private val FIRST_PORT = 10000 + private val workDir = Paths.get("work", localDir).toAbsolutePath() + + private val javaExe = if (UIUtil.isWindows) "java.exe" else "java" + private val javaPath = Paths.get(System.getProperty("java.home"), "bin", javaExe) + private val cordaPath = Paths.get("corda", "corda.jar").toAbsolutePath() + private val command = arrayOf(javaPath.toString(), "-jar", cordaPath.toString()) + + private val renderOptions = ConfigRenderOptions.defaults().setOriginComments(false) + private val nodes = ConcurrentHashMap() private val port = AtomicInteger(FIRST_PORT) fun validate(nodeData: NodeData): NodeConfig? { val config = NodeConfig( nodeData.legalName.value, + nodeData.nearestCity.value, nodeData.p2pPort.value, nodeData.artemisPort.value, nodeData.webPort.value @@ -28,4 +45,33 @@ class NodeController : Controller() { val nextPort: Int get() { return port.andIncrement } + fun runCorda(pty: R3Pty, config: NodeConfig): Boolean { + val nodeDir = workDir.resolve(config.key).toFile() + + if (nodeDir.mkdirs()) { + try { + // Write this nodes configuration file into its working directory. + val confFile = nodeDir.resolve("node.conf") + val fileData = config.toFileConfig + confFile.writeText(fileData.root().render(renderOptions)) + + pty.run(command, System.getenv(), nodeDir.toString()) + return true + } catch (e: Exception) { + log.severe("Failed to launch Corda:" + e) + return false + } + } else { + return false + } + } + + private val localDir: String + get() = SimpleDateFormat("yyyyMMddHHmmss") + .format(Date(ManagementFactory.getRuntimeMXBean().startTime)) + + init { + log.info("Working directory: " + workDir) + log.info("Java executable: " + javaPath) + } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt index 60a7240155..672cdfc344 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt @@ -5,9 +5,10 @@ import javafx.beans.property.SimpleStringProperty class NodeData { - var legalName : SimpleStringProperty = SimpleStringProperty("") - var p2pPort: SimpleIntegerProperty = SimpleIntegerProperty(0) - val artemisPort: SimpleIntegerProperty = SimpleIntegerProperty(0) - val webPort: SimpleIntegerProperty = SimpleIntegerProperty(0) + var legalName = SimpleStringProperty("") + val nearestCity = SimpleStringProperty("London") + var p2pPort = SimpleIntegerProperty(0) + val artemisPort = SimpleIntegerProperty(0) + val webPort = SimpleIntegerProperty(0) } \ No newline at end of file diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeDataModel.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeDataModel.kt index a58bc6b7ae..9791b468a8 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeDataModel.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeDataModel.kt @@ -5,6 +5,7 @@ import tornadofx.ItemViewModel class NodeDataModel : ItemViewModel(NodeData()) { val legalName = bind { item?.legalName } + val nearestCity = bind { item?.nearestCity } val p2pPort = bind { item?.p2pPort } val artemisPort = bind { item?.artemisPort } val webPort = bind { item?.webPort } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index 87514430f8..d3996198a5 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -33,6 +33,19 @@ class NodeTabView : Fragment() { } } } + field("Nearest City") { + textfield(model.nearestCity) { + minWidth = 200.0 + maxWidth = 200.0 + validator { + if (it.isNullOrBlank()) { + error("Nearest city is required") + } else { + null + } + } + } + } field("P2P Port") { textfield(model.p2pPort, NumberStringConverter(INTEGER_FORMAT)) { minWidth = 100.0 @@ -99,8 +112,15 @@ class NodeTabView : Fragment() { val config = controller.validate(model.item) if (config != null) { nodeConfigView.isVisible = false - nodeTab.text = config.name + nodeTab.text = config.legalName nodeTerminalView.open(config) + + nodeTab.setOnSelectionChanged { + if (nodeTab.isSelected) { + // Doesn't work yet + nodeTerminalView.refreshTerminal() + } + } } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt index c1b400d56c..961f47417c 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt @@ -11,6 +11,7 @@ import javafx.scene.layout.Priority import javafx.scene.layout.VBox import javax.swing.SwingUtilities import net.corda.demobench.model.NodeConfig +import net.corda.demobench.model.NodeController import net.corda.demobench.pty.R3Pty import net.corda.demobench.ui.PropertyLabel import tornadofx.Fragment @@ -19,6 +20,8 @@ import tornadofx.vgrow class NodeTerminalView : Fragment() { override val root by fxml() + private val controller by inject() + private val nodeName by fxid