mirror of
https://github.com/corda/corda.git
synced 2025-01-28 07:04:12 +00:00
Add initial form for configuring new nodes.
This commit is contained in:
parent
1bef874e27
commit
cc1d3e0ccf
@ -3,10 +3,12 @@ version '0.7-SNAPSHOT'
|
|||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.0.6'
|
ext.kotlin_version = '1.0.6'
|
||||||
|
ext.tornadofx_version = '1.6.0'
|
||||||
ext.jna_version = '4.1.0'
|
ext.jna_version = '4.1.0'
|
||||||
ext.purejavacomm_version = '0.0.17'
|
ext.purejavacomm_version = '0.0.17'
|
||||||
ext.guava_version = '14.0.1'
|
ext.guava_version = '14.0.1'
|
||||||
ext.slf4j_version = '1.7.22'
|
ext.slf4j_version = '1.7.22'
|
||||||
|
ext.logback_version = '1.1.3'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -18,8 +20,11 @@ buildscript {
|
|||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
|
mainClassName = 'net.corda.demobench.DemoBench'
|
||||||
|
applicationDefaultJvmArgs = ['-Djava.util.logging.config.class=net.corda.demobench.config.LoggingConfig']
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
flatDir {
|
flatDir {
|
||||||
@ -34,6 +39,8 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// TornadoFX: A lightweight Kotlin framework for working with JavaFX UI's.
|
||||||
|
compile "no.tornado:tornadofx:$tornadofx_version"
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
compile "net.java.dev.jna:jna:$jna_version"
|
compile "net.java.dev.jna:jna:$jna_version"
|
||||||
@ -42,12 +49,15 @@ dependencies {
|
|||||||
compile "com.sparetimelabs:purejavacomm:$purejavacomm_version"
|
compile "com.sparetimelabs:purejavacomm:$purejavacomm_version"
|
||||||
compile "org.slf4j:log4j-over-slf4j:$slf4j_version"
|
compile "org.slf4j:log4j-over-slf4j:$slf4j_version"
|
||||||
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
||||||
compile "ch.qos.logback:logback-classic:1.1.3"
|
compile "ch.qos.logback:logback-classic:$logback_version"
|
||||||
compile ":jediterm-terminal-2.5"
|
compile ":jediterm-terminal-2.5"
|
||||||
compile ':pty4j-0.7.2'
|
compile ':pty4j-0.7.2'
|
||||||
|
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.11'
|
testCompile group: 'junit', name: 'junit', version: '4.11'
|
||||||
|
}
|
||||||
// TornadoFX: A lightweight Kotlin framework for working with JavaFX UI's.
|
|
||||||
compile 'no.tornado:tornadofx:1.6.0'
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes 'Main-Class': mainClassName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,9 @@ public class R3Pty implements AutoCloseable {
|
|||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
LOG.info("Closing terminal '{}'", name);
|
LOG.info("Closing terminal '{}'", name);
|
||||||
terminal.close();
|
if (terminal.getTerminalStarter() != null) {
|
||||||
|
terminal.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package net.corda.demobench.model
|
||||||
|
|
||||||
|
class NodeConfig(name: String, p2pPort: Int, artemisPort: Int, webPort: Int) {
|
||||||
|
|
||||||
|
private var keyValue: String = toKey(name)
|
||||||
|
val key : String
|
||||||
|
get() { return keyValue }
|
||||||
|
|
||||||
|
private var nameValue: String = name
|
||||||
|
val name : String
|
||||||
|
get() { return nameValue }
|
||||||
|
|
||||||
|
private var p2pPortValue: Int = p2pPort
|
||||||
|
val p2pPort : Int
|
||||||
|
get() { return p2pPortValue }
|
||||||
|
|
||||||
|
private var artemisPortValue: Int = artemisPort
|
||||||
|
val artemisPort : Int
|
||||||
|
get() { return artemisPortValue }
|
||||||
|
|
||||||
|
private var webPortValue: Int = webPort
|
||||||
|
val webPort : Int
|
||||||
|
get() { return webPortValue }
|
||||||
|
|
||||||
|
private fun toKey(value: String): String {
|
||||||
|
return value.replace("\\s++", "").toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package net.corda.demobench.model
|
||||||
|
|
||||||
|
import tornadofx.Controller
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
class NodeController : Controller() {
|
||||||
|
private val FIRST_PORT = 10000
|
||||||
|
|
||||||
|
private val nodes = ConcurrentHashMap<String, NodeConfig>()
|
||||||
|
private val port = AtomicInteger(FIRST_PORT)
|
||||||
|
|
||||||
|
fun validate(nodeData: NodeData): NodeConfig? {
|
||||||
|
val config = NodeConfig(
|
||||||
|
nodeData.legalName.value,
|
||||||
|
nodeData.p2pPort.value,
|
||||||
|
nodeData.artemisPort.value,
|
||||||
|
nodeData.webPort.value
|
||||||
|
)
|
||||||
|
|
||||||
|
if (nodes.putIfAbsent(config.key, config) != null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextPort: Int
|
||||||
|
get() { return port.andIncrement }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package net.corda.demobench.model
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
|
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)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package net.corda.demobench.model
|
||||||
|
|
||||||
|
import tornadofx.ItemViewModel
|
||||||
|
|
||||||
|
class NodeDataModel : ItemViewModel<NodeData>(NodeData()) {
|
||||||
|
|
||||||
|
val legalName = bind { item?.legalName }
|
||||||
|
val p2pPort = bind { item?.p2pPort }
|
||||||
|
val artemisPort = bind { item?.artemisPort }
|
||||||
|
val webPort = bind { item?.webPort }
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,11 @@
|
|||||||
package net.corda.demobench.views
|
package net.corda.demobench.views
|
||||||
|
|
||||||
import com.jediterm.terminal.TerminalColor
|
|
||||||
import com.jediterm.terminal.TextStyle
|
|
||||||
import com.jediterm.terminal.ui.settings.DefaultSettingsProvider
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javafx.application.Platform
|
import javafx.application.Platform
|
||||||
import javafx.embed.swing.SwingNode
|
|
||||||
import javafx.scene.Parent
|
import javafx.scene.Parent
|
||||||
import javafx.scene.control.Button
|
import javafx.scene.control.Button
|
||||||
import javafx.scene.control.Tab
|
import javafx.scene.control.Tab
|
||||||
import javafx.scene.control.TabPane
|
import javafx.scene.control.TabPane
|
||||||
import javax.swing.SwingUtilities
|
|
||||||
import net.corda.demobench.pty.R3Pty
|
|
||||||
import net.corda.demobench.ui.CloseableTab
|
import net.corda.demobench.ui.CloseableTab
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
@ -22,8 +16,6 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
val addNodeButton by fxid<Button>()
|
val addNodeButton by fxid<Button>()
|
||||||
val nodeTabPane by fxid<TabPane>()
|
val nodeTabPane by fxid<TabPane>()
|
||||||
|
|
||||||
val settingsProvider = TerminalSettingsProvider()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
importStylesheet("/net/corda/demobench/style.css")
|
importStylesheet("/net/corda/demobench/style.css")
|
||||||
|
|
||||||
@ -35,7 +27,7 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addNodeButton.setOnAction {
|
addNodeButton.setOnAction {
|
||||||
val nodeTab = createNode()
|
val nodeTab = createNodeTab()
|
||||||
nodeTabPane.selectionModel.select(nodeTab)
|
nodeTabPane.selectionModel.select(nodeTab)
|
||||||
}
|
}
|
||||||
addNodeButton.fire()
|
addNodeButton.fire()
|
||||||
@ -47,40 +39,12 @@ class DemoBenchView : View("Corda Demo Bench") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNode(): CloseableTab {
|
fun createNodeTab(): CloseableTab {
|
||||||
val pty = R3Pty("Banksy", settingsProvider, java.awt.Dimension(160, 80))
|
val nodeTabView = find<NodeTabView>()
|
||||||
val nodeTabView = NodeTabView(pty.name)
|
val nodeTab = nodeTabView.nodeTab
|
||||||
val nodeTab = CloseableTab(pty.name, nodeTabView.root)
|
|
||||||
|
|
||||||
// Ensure that we close the terminal along with the tab.
|
|
||||||
nodeTab.setOnCloseRequest {
|
|
||||||
pty.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
val swingTerminal = SwingNode()
|
|
||||||
SwingUtilities.invokeLater({
|
|
||||||
swingTerminal.content = pty.terminal
|
|
||||||
swingTerminal.setOnMouseClicked {
|
|
||||||
swingTerminal.requestFocus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
nodeTabPane.tabs.add(nodeTab)
|
nodeTabPane.tabs.add(nodeTab)
|
||||||
nodeTab.content.add(swingTerminal)
|
|
||||||
|
|
||||||
pty.run("/bin/bash", "--login")
|
|
||||||
|
|
||||||
return nodeTab
|
return nodeTab
|
||||||
}
|
}
|
||||||
|
|
||||||
class TerminalSettingsProvider : DefaultSettingsProvider() {
|
|
||||||
override fun getDefaultStyle(): TextStyle {
|
|
||||||
return TextStyle(TerminalColor.WHITE, TerminalColor.BLACK)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun emulateX11CopyPaste(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,122 @@
|
|||||||
package net.corda.demobench.views
|
package net.corda.demobench.views
|
||||||
|
|
||||||
import javafx.scene.control.Button
|
import java.text.DecimalFormat
|
||||||
import javafx.scene.control.Label
|
import javafx.util.converter.NumberStringConverter
|
||||||
import javafx.scene.layout.Priority
|
import net.corda.demobench.model.NodeController
|
||||||
import javafx.scene.layout.VBox
|
import net.corda.demobench.model.NodeDataModel
|
||||||
import net.corda.demobench.ui.PropertyLabel
|
import net.corda.demobench.ui.CloseableTab
|
||||||
import tornadofx.Fragment
|
import tornadofx.*
|
||||||
import tornadofx.vgrow
|
|
||||||
|
|
||||||
class NodeTabView(name: String) : Fragment() {
|
class NodeTabView : Fragment() {
|
||||||
override val root by fxml<VBox>()
|
override val root = stackpane {}
|
||||||
|
|
||||||
val nodeName by fxid<Label>()
|
private val INTEGER_FORMAT = DecimalFormat()
|
||||||
val p2pPort by fxid<PropertyLabel>()
|
private val NOT_NUMBER = Regex("[^\\d]")
|
||||||
val states by fxid<PropertyLabel>()
|
|
||||||
val transactions by fxid<PropertyLabel>()
|
|
||||||
val balance by fxid<PropertyLabel>()
|
|
||||||
|
|
||||||
val viewDatabaseButton by fxid<Button>()
|
private val model = NodeDataModel()
|
||||||
val launchExplorerButton by fxid<Button>()
|
private val controller by inject<NodeController>()
|
||||||
|
|
||||||
|
private val nodeTerminalView = find<NodeTerminalView>()
|
||||||
|
private val nodeConfigView = pane {
|
||||||
|
form {
|
||||||
|
fieldset("Configuration") {
|
||||||
|
field("Node Name") {
|
||||||
|
textfield(model.legalName) {
|
||||||
|
minWidth = 200.0
|
||||||
|
maxWidth = 200.0
|
||||||
|
validator {
|
||||||
|
if (it.isNullOrBlank()) {
|
||||||
|
error("Node name is required")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field("P2P Port") {
|
||||||
|
textfield(model.p2pPort, NumberStringConverter(INTEGER_FORMAT)) {
|
||||||
|
minWidth = 100.0
|
||||||
|
maxWidth = 100.0
|
||||||
|
validator {
|
||||||
|
if ((it == null) || it.isEmpty()) {
|
||||||
|
error("Port number required")
|
||||||
|
} else if (it.contains(NOT_NUMBER)) {
|
||||||
|
error("Invalid port number")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field("Artemis Port") {
|
||||||
|
textfield(model.artemisPort, NumberStringConverter(INTEGER_FORMAT)) {
|
||||||
|
minWidth = 100.0
|
||||||
|
maxWidth = 100.0
|
||||||
|
validator {
|
||||||
|
if ((it == null) || it.isEmpty()) {
|
||||||
|
error("Port number required")
|
||||||
|
} else if (it.contains(NOT_NUMBER)) {
|
||||||
|
error("Invalid port number")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field("Web Port") {
|
||||||
|
textfield(model.webPort, NumberStringConverter(INTEGER_FORMAT)) {
|
||||||
|
minWidth = 100.0
|
||||||
|
maxWidth = 100.0
|
||||||
|
validator {
|
||||||
|
if ((it == null) || it.isEmpty()) {
|
||||||
|
error("Port number required")
|
||||||
|
} else if (it.contains(NOT_NUMBER)) {
|
||||||
|
error("Invalid port number")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset("Plugins") {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
button("Create Node") {
|
||||||
|
setOnAction() {
|
||||||
|
launch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodeTab = CloseableTab("New Node", root)
|
||||||
|
|
||||||
|
fun launch() {
|
||||||
|
model.commit()
|
||||||
|
val config = controller.validate(model.item)
|
||||||
|
if (config != null) {
|
||||||
|
nodeConfigView.isVisible = false
|
||||||
|
nodeTab.text = config.name
|
||||||
|
nodeTerminalView.open(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
nodeName.text = name
|
INTEGER_FORMAT.isGroupingUsed = false
|
||||||
root.vgrow = Priority.ALWAYS
|
|
||||||
|
// Ensure that we close the terminal along with the tab.
|
||||||
|
nodeTab.setOnCloseRequest {
|
||||||
|
nodeTerminalView.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
root.add(nodeConfigView)
|
||||||
|
root.add(nodeTerminalView)
|
||||||
|
|
||||||
|
model.p2pPort.value = controller.nextPort
|
||||||
|
model.artemisPort.value = controller.nextPort
|
||||||
|
model.webPort.value = controller.nextPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package net.corda.demobench.views
|
||||||
|
|
||||||
|
import com.jediterm.terminal.TerminalColor
|
||||||
|
import com.jediterm.terminal.TextStyle
|
||||||
|
import com.jediterm.terminal.ui.settings.DefaultSettingsProvider
|
||||||
|
import java.awt.Dimension
|
||||||
|
import javafx.embed.swing.SwingNode
|
||||||
|
import javafx.scene.control.Button
|
||||||
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.layout.Priority
|
||||||
|
import javafx.scene.layout.VBox
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
import net.corda.demobench.model.NodeConfig
|
||||||
|
import net.corda.demobench.pty.R3Pty
|
||||||
|
import net.corda.demobench.ui.PropertyLabel
|
||||||
|
import tornadofx.Fragment
|
||||||
|
import tornadofx.vgrow
|
||||||
|
|
||||||
|
class NodeTerminalView : Fragment() {
|
||||||
|
override val root by fxml<VBox>()
|
||||||
|
|
||||||
|
private val nodeName by fxid<Label>()
|
||||||
|
private val p2pPort by fxid<PropertyLabel>()
|
||||||
|
private val states by fxid<PropertyLabel>()
|
||||||
|
private val transactions by fxid<PropertyLabel>()
|
||||||
|
private val balance by fxid<PropertyLabel>()
|
||||||
|
|
||||||
|
private val viewDatabaseButton by fxid<Button>()
|
||||||
|
private val launchExplorerButton by fxid<Button>()
|
||||||
|
|
||||||
|
var pty : R3Pty? = null
|
||||||
|
|
||||||
|
fun open(config: NodeConfig) {
|
||||||
|
nodeName.text = config.name
|
||||||
|
p2pPort.value = config.p2pPort.toString()
|
||||||
|
|
||||||
|
val swingTerminal = SwingNode()
|
||||||
|
swingTerminal.setOnMouseClicked {
|
||||||
|
swingTerminal.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
root.children.add(swingTerminal)
|
||||||
|
root.isVisible = true
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater({
|
||||||
|
val r3pty = R3Pty(config.name, TerminalSettingsProvider(), Dimension(160, 80))
|
||||||
|
pty = r3pty
|
||||||
|
|
||||||
|
swingTerminal.content = r3pty.terminal
|
||||||
|
r3pty.run("/bin/bash", "--login")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close() {
|
||||||
|
pty?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
root.vgrow = Priority.ALWAYS
|
||||||
|
}
|
||||||
|
|
||||||
|
class TerminalSettingsProvider : DefaultSettingsProvider() {
|
||||||
|
override fun getDefaultStyle(): TextStyle {
|
||||||
|
return TextStyle(TerminalColor.WHITE, TerminalColor.BLACK)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun emulateX11CopyPaste(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import net.corda.demobench.ui.PropertyLabel?>
|
<?import net.corda.demobench.ui.PropertyLabel?>
|
||||||
|
|
||||||
<VBox prefHeight="953.0" prefWidth="1363.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1">
|
<VBox visible="false" prefHeight="953.0" prefWidth="1363.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<children>
|
<children>
|
||||||
<HBox prefHeight="95.0" prefWidth="800.0" spacing="15.0" styleClass="header">
|
<HBox prefHeight="95.0" prefWidth="800.0" spacing="15.0" styleClass="header">
|
||||||
<children>
|
<children>
|
Loading…
x
Reference in New Issue
Block a user