mirror of
https://github.com/corda/corda.git
synced 2025-01-20 03:36:29 +00:00
NodeRunner enhancements (#614)
* Use same java.home as caller in terminal window case, previously only headless case did that * Bump gradlePluginsVersion as I've changed NodeRunner On Linux: * Prefer JAVA_HOME over PATH * Use correct escaping * Start sh on failure so that terminals don't disappear taking error messages with them
This commit is contained in:
parent
ccb8827107
commit
45997ccd13
@ -1,4 +1,4 @@
|
||||
gradlePluginsVersion=0.11.2
|
||||
gradlePluginsVersion=0.12.0
|
||||
kotlinVersion=1.1.1
|
||||
guavaVersion=21.0
|
||||
bouncycastleVersion=1.56
|
||||
|
@ -9,5 +9,5 @@ cd "$basedir"
|
||||
if which osascript >/dev/null; then
|
||||
/usr/libexec/java_home -v 1.8 --exec java -jar runnodes.jar "$@"
|
||||
else
|
||||
java -jar runnodes.jar "$@"
|
||||
"${JAVA_HOME:+$JAVA_HOME/bin/}java" -jar runnodes.jar "$@"
|
||||
fi
|
||||
|
@ -3,119 +3,108 @@ package net.corda.plugins
|
||||
import java.awt.GraphicsEnvironment
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
|
||||
private val nodeJarName = "corda.jar"
|
||||
private val webJarName = "corda-webserver.jar"
|
||||
private val nodeConfName = "node.conf"
|
||||
private val HEADLESS_FLAG = "--headless"
|
||||
|
||||
private val os: OS by lazy {
|
||||
private val os by lazy {
|
||||
val osName = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH)
|
||||
if ((osName.indexOf("mac") >= 0) || (osName.indexOf("darwin") >= 0)) OS.MACOS
|
||||
else if (osName.indexOf("win") >= 0) OS.WINDOWS
|
||||
if ("mac" in osName || "darwin" in osName) OS.MACOS
|
||||
else if ("win" in osName) OS.WINDOWS
|
||||
else OS.LINUX
|
||||
}
|
||||
|
||||
private enum class OS { MACOS, WINDOWS, LINUX }
|
||||
|
||||
data class IncrementalPortAllocator(var basePort: Int = 5005) {
|
||||
fun next(): Int = basePort++
|
||||
private object debugPortAlloc {
|
||||
private var basePort = 5005
|
||||
internal fun next() = basePort++
|
||||
}
|
||||
|
||||
val debugPortAlloc: IncrementalPortAllocator = IncrementalPortAllocator()
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val startedProcesses = mutableListOf<Process>()
|
||||
val headless = GraphicsEnvironment.isHeadless() || (args.isNotEmpty() && args[0] == HEADLESS_FLAG)
|
||||
val workingDir = Paths.get(System.getProperty("user.dir")).toFile()
|
||||
val workingDir = File(System.getProperty("user.dir"))
|
||||
val javaArgs = args.filter { it != HEADLESS_FLAG }
|
||||
println("Starting nodes in $workingDir")
|
||||
|
||||
workingDir.list().map { File(workingDir, it) }.forEach {
|
||||
if (isNode(it)) {
|
||||
startedProcesses += startJarProcess(headless, it, nodeJarName, javaArgs)
|
||||
}
|
||||
|
||||
if (isWebserver(it)) {
|
||||
startedProcesses += startJarProcess(headless, it, webJarName, javaArgs)
|
||||
workingDir.listFiles { file -> file.isDirectory }.forEach { dir ->
|
||||
listOf(NodeJarType, WebJarType).forEach { jarType ->
|
||||
jarType.acceptDirAndStartProcess(dir, headless, javaArgs)?.let { startedProcesses += it }
|
||||
}
|
||||
}
|
||||
|
||||
println("Started ${startedProcesses.size} processes")
|
||||
println("Finished starting nodes")
|
||||
}
|
||||
|
||||
private fun startJarProcess(headless: Boolean, dir: File, jarName: String, javaArgs: List<String>) : Process {
|
||||
val runJar = getJarRunner(headless)
|
||||
private abstract class JarType(private val jarName: String) {
|
||||
internal abstract fun acceptNodeConf(nodeConf: File): Boolean
|
||||
internal fun acceptDirAndStartProcess(dir: File, headless: Boolean, javaArgs: List<String>): Process? {
|
||||
if (!File(dir, jarName).exists()) {
|
||||
return null
|
||||
}
|
||||
if (!File(dir, "node.conf").let { it.exists() && acceptNodeConf(it) }) {
|
||||
return null
|
||||
}
|
||||
val debugPort = debugPortAlloc.next()
|
||||
println("Starting $jarName in $dir on debug port $debugPort")
|
||||
val proc = runJar(jarName, dir, javaArgs, debugPort)
|
||||
val process = (if (headless) ::HeadlessJavaCommand else ::TerminalWindowJavaCommand)(jarName, dir, debugPort, javaArgs).start()
|
||||
if (os == OS.MACOS) Thread.sleep(1000)
|
||||
return proc
|
||||
return process
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNode(maybeNodeDir: File) = maybeNodeDir.isDirectory
|
||||
&& File(maybeNodeDir, nodeJarName).exists()
|
||||
&& File(maybeNodeDir, nodeConfName).exists()
|
||||
|
||||
private fun isWebserver(maybeWebserverDir: File) = maybeWebserverDir.isDirectory
|
||||
&& File(maybeWebserverDir, webJarName).exists()
|
||||
&& File(maybeWebserverDir, nodeConfName).exists()
|
||||
&& hasWebserverPort(maybeWebserverDir)
|
||||
|
||||
// TODO: Add a webserver.conf, or use TypeSafe config instead of this hack
|
||||
private fun hasWebserverPort(nodeConfDir: File) = Files.readAllLines(File(nodeConfDir, nodeConfName).toPath()).joinToString { it }.contains("webAddress")
|
||||
|
||||
private fun getDebugPortArg(debugPort: Int?) = if (debugPort != null) {
|
||||
listOf("-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort")
|
||||
} else {
|
||||
emptyList()
|
||||
private object NodeJarType : JarType("corda.jar") {
|
||||
override fun acceptNodeConf(nodeConf: File) = true
|
||||
}
|
||||
|
||||
private fun getJarRunner(headless: Boolean): (String, File, List<String>, Int?) -> Process = if (headless) ::execJar else ::execJarInTerminalWindow
|
||||
|
||||
private fun execJar(jarName: String, dir: File, args: List<String> = listOf(), debugPort: Int?): Process {
|
||||
val nodeName = dir.toPath().fileName
|
||||
val separator = System.getProperty("file.separator")
|
||||
val path = System.getProperty("java.home") + separator + "bin" + separator + "java"
|
||||
val builder = ProcessBuilder(listOf(path, "-Dname=$nodeName") + getDebugPortArg(debugPort) + listOf("-jar", jarName, "--no-local-shell") + args)
|
||||
builder.redirectError(Paths.get("error.${dir.toPath().fileName}.log").toFile())
|
||||
builder.inheritIO()
|
||||
builder.directory(dir)
|
||||
return builder.start()
|
||||
private object WebJarType : JarType("corda-webserver.jar") {
|
||||
// TODO: Add a webserver.conf, or use TypeSafe config instead of this hack
|
||||
override fun acceptNodeConf(nodeConf: File) = Files.lines(nodeConf.toPath()).anyMatch { "webAddress" in it }
|
||||
}
|
||||
|
||||
private fun execJarInTerminalWindow(jarName: String, dir: File, args: List<String> = listOf(), debugPort: Int?): Process {
|
||||
val nodeName = "${dir.toPath().fileName}-$jarName"
|
||||
val javaCmd = (listOf("java", "-Dname=$nodeName") + getDebugPortArg(debugPort) + listOf("-jar", jarName) + args).joinToString(" ") { it }
|
||||
val builder = when (os) {
|
||||
OS.MACOS -> ProcessBuilder(
|
||||
"osascript", "-e",
|
||||
"""tell app "Terminal"
|
||||
private abstract class JavaCommand(jarName: String, internal val dir: File, debugPort: Int?, internal val nodeName: String, init: MutableList<String>.() -> Unit, args: List<String>) {
|
||||
internal val command: List<String> = mutableListOf<String>().apply {
|
||||
add(File(File(System.getProperty("java.home"), "bin"), "java").path)
|
||||
add("-Dname=$nodeName")
|
||||
null != debugPort && add("-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort")
|
||||
add("-jar"); add(jarName)
|
||||
init()
|
||||
addAll(args)
|
||||
}
|
||||
|
||||
internal abstract fun processBuilder(): ProcessBuilder
|
||||
internal fun start() = processBuilder().directory(dir).start()
|
||||
}
|
||||
|
||||
private class HeadlessJavaCommand(jarName: String, dir: File, debugPort: Int?, args: List<String>) : JavaCommand(jarName, dir, debugPort, dir.name, { add("--no-local-shell") }, args) {
|
||||
override fun processBuilder() = ProcessBuilder(command).redirectError(File("error.$nodeName.log")).inheritIO()
|
||||
}
|
||||
|
||||
private class TerminalWindowJavaCommand(jarName: String, dir: File, debugPort: Int?, args: List<String>) : JavaCommand(jarName, dir, debugPort, "${dir.name}-$jarName", {}, args) {
|
||||
override fun processBuilder() = ProcessBuilder(when (os) {
|
||||
OS.MACOS -> {
|
||||
listOf("osascript", "-e", """tell app "Terminal"
|
||||
activate
|
||||
tell app "System Events" to tell process "Terminal" to keystroke "t" using command down
|
||||
delay 0.5
|
||||
do script "bash -c 'cd $dir; /usr/libexec/java_home -v 1.8 --exec $javaCmd && exit'" in selected tab of the front window
|
||||
end tell"""
|
||||
)
|
||||
OS.WINDOWS -> ProcessBuilder(
|
||||
"cmd"
|
||||
, "/C", "start $javaCmd"
|
||||
)
|
||||
do script "bash -c 'cd $dir; /usr/libexec/java_home -v 1.8 --exec ${command.joinToString(" ")} && exit'" in selected tab of the front window
|
||||
end tell""")
|
||||
}
|
||||
OS.WINDOWS -> {
|
||||
listOf("cmd", "/C", "start ${command.joinToString(" ")}")
|
||||
}
|
||||
OS.LINUX -> {
|
||||
val isTmux = System.getenv("TMUX")?.isNotEmpty() ?: false
|
||||
if (isTmux) {
|
||||
ProcessBuilder(
|
||||
"tmux", "new-window", "-n", nodeName, javaCmd
|
||||
)
|
||||
val command = "${unixCommand()} || sh"
|
||||
if (isTmux()) {
|
||||
listOf("tmux", "new-window", "-n", nodeName, command)
|
||||
} else {
|
||||
ProcessBuilder(
|
||||
"xterm", "-T", nodeName, "-e", javaCmd
|
||||
)
|
||||
listOf("xterm", "-T", nodeName, "-e", command)
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.directory(dir).start()
|
||||
})
|
||||
|
||||
private fun unixCommand() = command.map(::quotedFormOf).joinToString(" ")
|
||||
}
|
||||
|
||||
private fun quotedFormOf(text: String) = "'${text.replace("'", "'\\''")}'" // Suitable for UNIX shells.
|
||||
private fun isTmux() = System.getenv("TMUX")?.isNotEmpty() ?: false
|
||||
|
Loading…
Reference in New Issue
Block a user