mirror of
https://github.com/corda/corda.git
synced 2025-01-14 16:59:52 +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("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
|
||||
|
@ -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<String, NodeConfig>()
|
||||
private val nodes = LinkedHashMap<String, NodeConfig>()
|
||||
private val port = AtomicInteger(FIRST_PORT)
|
||||
|
||||
private var networkMapConfig: NetworkMapConfig? = null
|
||||
|
||||
val activeNodes: List<NodeConfig> get() = nodes.values.filter {
|
||||
it.state == NodeState.RUNNING
|
||||
(it.state == NodeState.RUNNING) || (it.state == NodeState.STARTING)
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -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<Path, BasicFileAttributes> {
|
||||
override fun test(p: Path?, attr: BasicFileAttributes?) = "node.conf" == p?.fileName.toString()
|
||||
}
|
||||
|
||||
private val jvm by inject<JVMConfig>()
|
||||
private val baseDir = jvm.userHome.resolve("demobench")
|
||||
private val nodeController by inject<NodeController>()
|
||||
@ -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<NodeConfig>? {
|
||||
val chosen = chooser.showOpenDialog(null) ?: return null
|
||||
log.info("Selected profile: ${chosen}")
|
||||
log.info("Selected profile: $chosen")
|
||||
|
||||
val configs = LinkedList<NodeConfig>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<NodeRPC>()
|
||||
val ONE_SECOND = SECONDS.toMillis(1)
|
||||
}
|
||||
|
@ -23,39 +23,51 @@ class DemoBenchView : View("Corda Demo Bench") {
|
||||
private val addNodeButton by fxid<Button>()
|
||||
private val nodeTabPane by fxid<TabPane>()
|
||||
private val menuOpen by fxid<MenuItem>()
|
||||
private val menuSave by fxid<MenuItem>()
|
||||
private val menuSaveAs by fxid<MenuItem>()
|
||||
|
||||
init {
|
||||
importStylesheet("/net/corda/demobench/style.css")
|
||||
|
||||
primaryStage.setOnCloseRequest {
|
||||
log.info("Exiting")
|
||||
configureShutdown()
|
||||
|
||||
// Prevent any new NodeTabViews from being created.
|
||||
addNodeButton.isDisable = true
|
||||
configureProfileSaveAs()
|
||||
configureProfileOpen()
|
||||
|
||||
closeAllTabs()
|
||||
Platform.exit()
|
||||
}
|
||||
configureAddNode()
|
||||
}
|
||||
|
||||
menuSaveAs.setOnAction {
|
||||
profileController.saveAs()
|
||||
}
|
||||
menuSave.setOnAction {
|
||||
profileController.save()
|
||||
}
|
||||
menuOpen.setOnAction {
|
||||
try {
|
||||
val profile = profileController.openProfile()
|
||||
if (profile != null) {
|
||||
loadProfile(profile)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ExceptionDialog(e).apply { initOwner(root.scene.window) }.showAndWait()
|
||||
private fun configureShutdown() = primaryStage.setOnCloseRequest {
|
||||
log.info("Exiting")
|
||||
|
||||
// Prevent any new NodeTabViews from being created.
|
||||
addNodeButton.isDisable = true
|
||||
|
||||
closeAllTabs()
|
||||
Platform.exit()
|
||||
}
|
||||
|
||||
private fun configureProfileSaveAs() = menuSaveAs.setOnAction {
|
||||
try {
|
||||
if (profileController.saveProfile()) {
|
||||
menuSaveAs.isDisable = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ExceptionDialog(e).apply { initOwner(root.scene.window) }.showAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureProfileOpen() = menuOpen.setOnAction {
|
||||
try {
|
||||
val profile = profileController.openProfile()
|
||||
if (profile != null) {
|
||||
loadProfile(profile)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ExceptionDialog(e).apply { initOwner(root.scene.window) }.showAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureAddNode() {
|
||||
addNodeButton.setOnAction {
|
||||
val nodeTabView = createNodeTabView(true)
|
||||
nodeTabPane.selectionModel.select(nodeTabView.nodeTab)
|
||||
@ -66,19 +78,17 @@ class DemoBenchView : View("Corda Demo Bench") {
|
||||
addNodeButton.fire()
|
||||
}
|
||||
|
||||
private fun closeAllTabs() {
|
||||
ArrayList<Tab>(nodeTabPane.tabs).forEach {
|
||||
(it as CloseableTab).requestClose()
|
||||
}
|
||||
private fun closeAllTabs() = ArrayList<Tab>(nodeTabPane.tabs).forEach {
|
||||
(it as CloseableTab).requestClose()
|
||||
}
|
||||
|
||||
fun createNodeTabView(showConfig: Boolean): NodeTabView {
|
||||
private fun createNodeTabView(showConfig: Boolean): NodeTabView {
|
||||
val nodeTabView = find<NodeTabView>(mapOf("showConfig" to showConfig))
|
||||
nodeTabPane.tabs.add(nodeTabView.nodeTab)
|
||||
return nodeTabView
|
||||
}
|
||||
|
||||
fun loadProfile(nodes: List<NodeConfig>) {
|
||||
private fun loadProfile(nodes: List<NodeConfig>) {
|
||||
closeAllTabs()
|
||||
nodeController.reset()
|
||||
|
||||
@ -90,6 +100,16 @@ class DemoBenchView : View("Corda Demo Bench") {
|
||||
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() {
|
||||
addNodeButton.isDisable = false
|
||||
}
|
||||
|
@ -155,6 +155,7 @@ class NodeTabView : Fragment() {
|
||||
if (model.validate()) {
|
||||
launch()
|
||||
main.enableAddNodes()
|
||||
main.enableSaveProfile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,7 @@
|
||||
<MenuBar>
|
||||
<Menu text="File">
|
||||
<MenuItem fx:id="menuOpen" text="Open"/>
|
||||
<MenuItem fx:id="menuSave" text="Save"/>
|
||||
<MenuItem fx:id="menuSaveAs" text="Save As"/>
|
||||
<MenuItem fx:id="menuSaveAs" disable="true" text="Save As"/>
|
||||
</Menu>
|
||||
</MenuBar>
|
||||
<StackPane>
|
||||
|
Loading…
Reference in New Issue
Block a user