mirror of
https://github.com/corda/corda.git
synced 2025-01-15 09:20:22 +00:00
CORPRIV-661: Implement saving profiles.
This commit is contained in:
parent
ddd8d6a513
commit
0f73b68d39
@ -66,7 +66,7 @@ class NodeConfig(
|
|||||||
.withValue("h2port", valueFor(h2Port))
|
.withValue("h2port", valueFor(h2Port))
|
||||||
.withValue("useTestClock", valueFor(true))
|
.withValue("useTestClock", valueFor(true))
|
||||||
|
|
||||||
fun toText() = toFileConfig().root().render(renderOptions)
|
fun toText(): String = toFileConfig().root().render(renderOptions)
|
||||||
|
|
||||||
fun moveTo(baseDir: Path) = NodeConfig(
|
fun moveTo(baseDir: Path) = NodeConfig(
|
||||||
baseDir, legalName, artemisPort, nearestCity, webPort, h2Port, extraServices, users
|
baseDir, legalName, artemisPort, nearestCity, webPort, h2Port, extraServices, users
|
||||||
|
@ -5,7 +5,6 @@ import java.lang.management.ManagementFactory
|
|||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import net.corda.demobench.pty.R3Pty
|
import net.corda.demobench.pty.R3Pty
|
||||||
import tornadofx.Controller
|
import tornadofx.Controller
|
||||||
@ -27,13 +26,13 @@ class NodeController : Controller() {
|
|||||||
private val cordaPath = jvm.applicationDir.resolve("corda").resolve("corda.jar")
|
private val cordaPath = jvm.applicationDir.resolve("corda").resolve("corda.jar")
|
||||||
private val command = jvm.commandFor(cordaPath)
|
private val command = jvm.commandFor(cordaPath)
|
||||||
|
|
||||||
private val nodes = ConcurrentHashMap<String, NodeConfig>()
|
private val nodes = LinkedHashMap<String, NodeConfig>()
|
||||||
private val port = AtomicInteger(FIRST_PORT)
|
private val port = AtomicInteger(FIRST_PORT)
|
||||||
|
|
||||||
private var networkMapConfig: NetworkMapConfig? = null
|
private var networkMapConfig: NetworkMapConfig? = null
|
||||||
|
|
||||||
val activeNodes: List<NodeConfig> get() = nodes.values.filter {
|
val activeNodes: List<NodeConfig> get() = nodes.values.filter {
|
||||||
it.state == NodeState.RUNNING
|
(it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -3,10 +3,13 @@ package net.corda.demobench.profile
|
|||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import java.nio.file.FileSystems
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.net.URI
|
||||||
import java.nio.file.Path
|
import java.nio.charset.StandardCharsets.UTF_8
|
||||||
|
import java.nio.file.*
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.function.BiPredicate
|
||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
import javafx.stage.FileChooser.ExtensionFilter
|
import javafx.stage.FileChooser.ExtensionFilter
|
||||||
import net.corda.demobench.model.*
|
import net.corda.demobench.model.*
|
||||||
@ -14,6 +17,10 @@ import tornadofx.Controller
|
|||||||
|
|
||||||
class ProfileController : Controller() {
|
class ProfileController : Controller() {
|
||||||
|
|
||||||
|
private companion object ConfigAcceptor : BiPredicate<Path, BasicFileAttributes> {
|
||||||
|
override fun test(p: Path?, attr: BasicFileAttributes?) = "node.conf" == p?.fileName.toString()
|
||||||
|
}
|
||||||
|
|
||||||
private val jvm by inject<JVMConfig>()
|
private val jvm by inject<JVMConfig>()
|
||||||
private val baseDir = jvm.userHome.resolve("demobench")
|
private val baseDir = jvm.userHome.resolve("demobench")
|
||||||
private val nodeController by inject<NodeController>()
|
private val nodeController by inject<NodeController>()
|
||||||
@ -21,30 +28,47 @@ class ProfileController : Controller() {
|
|||||||
private val chooser = FileChooser()
|
private val chooser = FileChooser()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
chooser.title = "DemoBench Profiles"
|
||||||
chooser.initialDirectory = baseDir.toFile()
|
chooser.initialDirectory = baseDir.toFile()
|
||||||
chooser.extensionFilters.add(ExtensionFilter("DemoBench profiles (*.zip)", "*.zip", "*.ZIP"))
|
chooser.extensionFilters.add(ExtensionFilter("DemoBench profiles (*.zip)", "*.zip", "*.ZIP"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveAs() {
|
fun saveProfile(): Boolean {
|
||||||
log.info("Save as")
|
var target = chooser.showSaveDialog(null) ?: return false
|
||||||
|
if (target.extension.isEmpty()) {
|
||||||
|
target = File(target.parent, target.name + ".zip")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun save() {
|
log.info("Save profile as: $target")
|
||||||
log.info("Save")
|
|
||||||
|
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<NodeConfig>? {
|
fun openProfile(): List<NodeConfig>? {
|
||||||
val chosen = chooser.showOpenDialog(null) ?: return null
|
val chosen = chooser.showOpenDialog(null) ?: return null
|
||||||
log.info("Selected profile: ${chosen}")
|
log.info("Selected profile: $chosen")
|
||||||
|
|
||||||
val configs = LinkedList<NodeConfig>()
|
val configs = LinkedList<NodeConfig>()
|
||||||
|
|
||||||
FileSystems.newFileSystem(chosen.toPath(), null).use {
|
FileSystems.newFileSystem(chosen.toPath(), null).use {
|
||||||
fs -> fs.rootDirectories.forEach {
|
fs -> fs.rootDirectories.forEach {
|
||||||
root -> Files.walk(root).forEach {
|
root -> Files.find(root, 2, ConfigAcceptor).forEach {
|
||||||
if ((it.nameCount == 2) && ("node.conf" == it.fileName.toString())) {
|
|
||||||
try {
|
try {
|
||||||
configs.add(toNodeConfig(parse(it)))
|
// 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) {
|
} catch (e: Exception) {
|
||||||
log.severe("Failed to parse '$it': ${e.message}")
|
log.severe("Failed to parse '$it': ${e.message}")
|
||||||
throw e
|
throw e
|
||||||
@ -52,7 +76,6 @@ class ProfileController : Controller() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return configs
|
return configs
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import net.corda.node.services.messaging.CordaRPCClient
|
|||||||
|
|
||||||
class NodeRPC(config: NodeConfig, start: () -> Unit, invoke: (CordaRPCOps) -> Unit): AutoCloseable {
|
class NodeRPC(config: NodeConfig, start: () -> Unit, invoke: (CordaRPCOps) -> Unit): AutoCloseable {
|
||||||
|
|
||||||
private companion object Data {
|
private companion object {
|
||||||
val log = loggerFor<NodeRPC>()
|
val log = loggerFor<NodeRPC>()
|
||||||
val ONE_SECOND = SECONDS.toMillis(1)
|
val ONE_SECOND = SECONDS.toMillis(1)
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,20 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
private val addNodeButton by fxid<Button>()
|
private val addNodeButton by fxid<Button>()
|
||||||
private val nodeTabPane by fxid<TabPane>()
|
private val nodeTabPane by fxid<TabPane>()
|
||||||
private val menuOpen by fxid<MenuItem>()
|
private val menuOpen by fxid<MenuItem>()
|
||||||
private val menuSave by fxid<MenuItem>()
|
|
||||||
private val menuSaveAs by fxid<MenuItem>()
|
private val menuSaveAs by fxid<MenuItem>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
importStylesheet("/net/corda/demobench/style.css")
|
importStylesheet("/net/corda/demobench/style.css")
|
||||||
|
|
||||||
primaryStage.setOnCloseRequest {
|
configureShutdown()
|
||||||
|
|
||||||
|
configureProfileSaveAs()
|
||||||
|
configureProfileOpen()
|
||||||
|
|
||||||
|
configureAddNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureShutdown() = primaryStage.setOnCloseRequest {
|
||||||
log.info("Exiting")
|
log.info("Exiting")
|
||||||
|
|
||||||
// Prevent any new NodeTabViews from being created.
|
// Prevent any new NodeTabViews from being created.
|
||||||
@ -39,13 +46,17 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
Platform.exit()
|
Platform.exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
menuSaveAs.setOnAction {
|
private fun configureProfileSaveAs() = menuSaveAs.setOnAction {
|
||||||
profileController.saveAs()
|
try {
|
||||||
|
if (profileController.saveProfile()) {
|
||||||
|
menuSaveAs.isDisable = true
|
||||||
}
|
}
|
||||||
menuSave.setOnAction {
|
} catch (e: Exception) {
|
||||||
profileController.save()
|
ExceptionDialog(e).apply { initOwner(root.scene.window) }.showAndWait()
|
||||||
}
|
}
|
||||||
menuOpen.setOnAction {
|
}
|
||||||
|
|
||||||
|
private fun configureProfileOpen() = menuOpen.setOnAction {
|
||||||
try {
|
try {
|
||||||
val profile = profileController.openProfile()
|
val profile = profileController.openProfile()
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
@ -56,6 +67,7 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun configureAddNode() {
|
||||||
addNodeButton.setOnAction {
|
addNodeButton.setOnAction {
|
||||||
val nodeTabView = createNodeTabView(true)
|
val nodeTabView = createNodeTabView(true)
|
||||||
nodeTabPane.selectionModel.select(nodeTabView.nodeTab)
|
nodeTabPane.selectionModel.select(nodeTabView.nodeTab)
|
||||||
@ -66,19 +78,17 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
addNodeButton.fire()
|
addNodeButton.fire()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun closeAllTabs() {
|
private fun closeAllTabs() = ArrayList<Tab>(nodeTabPane.tabs).forEach {
|
||||||
ArrayList<Tab>(nodeTabPane.tabs).forEach {
|
|
||||||
(it as CloseableTab).requestClose()
|
(it as CloseableTab).requestClose()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun createNodeTabView(showConfig: Boolean): NodeTabView {
|
private fun createNodeTabView(showConfig: Boolean): NodeTabView {
|
||||||
val nodeTabView = find<NodeTabView>(mapOf("showConfig" to showConfig))
|
val nodeTabView = find<NodeTabView>(mapOf("showConfig" to showConfig))
|
||||||
nodeTabPane.tabs.add(nodeTabView.nodeTab)
|
nodeTabPane.tabs.add(nodeTabView.nodeTab)
|
||||||
return nodeTabView
|
return nodeTabView
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadProfile(nodes: List<NodeConfig>) {
|
private fun loadProfile(nodes: List<NodeConfig>) {
|
||||||
closeAllTabs()
|
closeAllTabs()
|
||||||
nodeController.reset()
|
nodeController.reset()
|
||||||
|
|
||||||
@ -90,6 +100,16 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
enableAddNodes()
|
enableAddNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the "save profile" menu item.
|
||||||
|
*/
|
||||||
|
fun enableSaveProfile() {
|
||||||
|
menuSaveAs.isDisable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the button that allows us to create a new node.
|
||||||
|
*/
|
||||||
fun enableAddNodes() {
|
fun enableAddNodes() {
|
||||||
addNodeButton.isDisable = false
|
addNodeButton.isDisable = false
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,7 @@ class NodeTabView : Fragment() {
|
|||||||
if (model.validate()) {
|
if (model.validate()) {
|
||||||
launch()
|
launch()
|
||||||
main.enableAddNodes()
|
main.enableAddNodes()
|
||||||
|
main.enableSaveProfile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
<MenuBar>
|
<MenuBar>
|
||||||
<Menu text="File">
|
<Menu text="File">
|
||||||
<MenuItem fx:id="menuOpen" text="Open"/>
|
<MenuItem fx:id="menuOpen" text="Open"/>
|
||||||
<MenuItem fx:id="menuSave" text="Save"/>
|
<MenuItem fx:id="menuSaveAs" disable="true" text="Save As"/>
|
||||||
<MenuItem fx:id="menuSaveAs" text="Save As"/>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
<StackPane>
|
<StackPane>
|
||||||
|
Loading…
Reference in New Issue
Block a user