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 7c6aac3464..0034101c98 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 @@ -66,7 +66,7 @@ class NodeConfig( .withValue("h2port", valueFor(h2Port)) .withValue("useTestClock", valueFor(true)) - fun toText() = toFileConfig().root().render(renderOptions) + fun toText(): String = toFileConfig().root().render(renderOptions) fun moveTo(baseDir: Path) = NodeConfig( baseDir, legalName, artemisPort, nearestCity, webPort, h2Port, extraServices, users 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 a946f795b6..d8589a3a89 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 @@ -5,7 +5,6 @@ import java.lang.management.ManagementFactory import java.net.ServerSocket 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 @@ -27,13 +26,13 @@ class NodeController : Controller() { private val cordaPath = jvm.applicationDir.resolve("corda").resolve("corda.jar") private val command = jvm.commandFor(cordaPath) - private val nodes = ConcurrentHashMap() + private val nodes = LinkedHashMap() private val port = AtomicInteger(FIRST_PORT) private var networkMapConfig: NetworkMapConfig? = null val activeNodes: List get() = nodes.values.filter { - it.state == NodeState.RUNNING + (it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING) } init { diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/profile/ProfileController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/profile/ProfileController.kt index 2bd2024d96..8e5438f19f 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/profile/ProfileController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/profile/ProfileController.kt @@ -3,10 +3,13 @@ package net.corda.demobench.profile import com.google.common.net.HostAndPort import com.typesafe.config.Config import com.typesafe.config.ConfigFactory -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Path +import java.io.File +import java.net.URI +import java.nio.charset.StandardCharsets.UTF_8 +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes import java.util.* +import java.util.function.BiPredicate import javafx.stage.FileChooser import javafx.stage.FileChooser.ExtensionFilter import net.corda.demobench.model.* @@ -14,6 +17,10 @@ import tornadofx.Controller class ProfileController : Controller() { + private companion object ConfigAcceptor : BiPredicate { + override fun test(p: Path?, attr: BasicFileAttributes?) = "node.conf" == p?.fileName.toString() + } + private val jvm by inject() private val baseDir = jvm.userHome.resolve("demobench") private val nodeController by inject() @@ -21,34 +28,50 @@ class ProfileController : Controller() { private val chooser = FileChooser() init { + chooser.title = "DemoBench Profiles" chooser.initialDirectory = baseDir.toFile() chooser.extensionFilters.add(ExtensionFilter("DemoBench profiles (*.zip)", "*.zip", "*.ZIP")) } - fun saveAs() { - log.info("Save as") - } + fun saveProfile(): Boolean { + var target = chooser.showSaveDialog(null) ?: return false + if (target.extension.isEmpty()) { + target = File(target.parent, target.name + ".zip") + } - fun save() { - log.info("Save") + log.info("Save profile as: $target") + + val configs = nodeController.activeNodes + + FileSystems.newFileSystem(URI.create("jar:" + target.toURI()), mapOf("create" to "true")).use { + fs -> configs.forEach { it -> + val nodeDir = Files.createDirectories(fs.getPath(it.key)) + val conf = Files.write(nodeDir.resolve("node.conf"), it.toText().toByteArray(UTF_8)) + log.info("Wrote: $conf") + } + } + + return true } fun openProfile(): List? { val chosen = chooser.showOpenDialog(null) ?: return null - log.info("Selected profile: ${chosen}") + log.info("Selected profile: $chosen") val configs = LinkedList() FileSystems.newFileSystem(chosen.toPath(), null).use { fs -> fs.rootDirectories.forEach { - root -> Files.walk(root).forEach { - if ((it.nameCount == 2) && ("node.conf" == it.fileName.toString())) { - try { - configs.add(toNodeConfig(parse(it))) - } catch (e: Exception) { - log.severe("Failed to parse '$it': ${e.message}") - throw e - } + root -> Files.find(root, 2, ConfigAcceptor).forEach { + try { + // Java seems to "walk" through the ZIP file backwards. + // So add new config to the front of the list, so that + // our final list is ordered to match the file. + configs.addFirst(toNodeConfig(parse(it))) + log.info("Loaded: $it") + } catch (e: Exception) { + log.severe("Failed to parse '$it': ${e.message}") + throw e } } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt index abfec68131..d88989739d 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/rpc/NodeRPC.kt @@ -10,7 +10,7 @@ import net.corda.node.services.messaging.CordaRPCClient class NodeRPC(config: NodeConfig, start: () -> Unit, invoke: (CordaRPCOps) -> Unit): AutoCloseable { - private companion object Data { + private companion object { val log = loggerFor() val ONE_SECOND = SECONDS.toMillis(1) } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/DemoBenchView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/DemoBenchView.kt index dd262976d4..cef277c1b8 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/DemoBenchView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/DemoBenchView.kt @@ -23,39 +23,51 @@ class DemoBenchView : View("Corda Demo Bench") { private val addNodeButton by fxid