From 81dcde99bf710ef1cb7680af3ab0a653ca4356ea Mon Sep 17 00:00:00 2001 From: exfalso <0slemi0@gmail.com> Date: Mon, 27 Mar 2017 15:36:56 +0100 Subject: [PATCH] #340: Factor out jvm process start into utility function --- .../corda/core/utilities/ProcessUtilities.kt | 47 +++++++++ .../kotlin/net/corda/node/driver/Driver.kt | 99 +++++++------------ .../kotlin/net/corda/testing/JVMSpawner.kt | 34 ------- .../net/corda/verifier/VerifierDriver.kt | 40 +++----- 4 files changed, 96 insertions(+), 124 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/utilities/ProcessUtilities.kt delete mode 100644 test-utils/src/main/kotlin/net/corda/testing/JVMSpawner.kt diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProcessUtilities.kt b/core/src/main/kotlin/net/corda/core/utilities/ProcessUtilities.kt new file mode 100644 index 0000000000..ef24005fdb --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/utilities/ProcessUtilities.kt @@ -0,0 +1,47 @@ +package net.corda.core.utilities + +import java.nio.file.Path + +object ProcessUtilities { + inline fun startJavaProcess( + arguments: List, + jdwpPort: Int? = null, + extraJvmArguments: List = emptyList(), + inheritIO: Boolean = true, + errorLogPath: Path? = null, + workingDirectory: Path? = null + ): Process { + return startJavaProcess(C::class.java.name, arguments, jdwpPort, extraJvmArguments, inheritIO, errorLogPath, workingDirectory) + } + + fun startJavaProcess( + className: String, + arguments: List, + jdwpPort: Int? = null, + extraJvmArguments: List = emptyList(), + inheritIO: Boolean = true, + errorLogPath: Path? = null, + workingDirectory: Path? = null + ): Process { + val separator = System.getProperty("file.separator") + val classpath = System.getProperty("java.class.path") + val javaPath = System.getProperty("java.home") + separator + "bin" + separator + "java" + val debugPortArgument = if (jdwpPort == null) { + listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$jdwpPort") + } else { + emptyList() + } + + val allArguments = listOf(javaPath) + + debugPortArgument + + listOf("-Xmx200m", "-XX:+UseG1GC") + + extraJvmArguments + + listOf("-cp", classpath, className) + + arguments.toList() + return ProcessBuilder(allArguments).apply { + if (errorLogPath != null) redirectError(errorLogPath.toFile()) + if (inheritIO) inheritIO() + if (workingDirectory != null) directory(workingDirectory.toFile()) + }.start() + } +} diff --git a/node/src/main/kotlin/net/corda/node/driver/Driver.kt b/node/src/main/kotlin/net/corda/node/driver/Driver.kt index aa5df87d5e..fee539e3ba 100644 --- a/node/src/main/kotlin/net/corda/node/driver/Driver.kt +++ b/node/src/main/kotlin/net/corda/node/driver/Driver.kt @@ -1,6 +1,7 @@ @file:JvmName("Driver") package net.corda.node.driver +import co.paralleluniverse.common.util.ProcessUtil import com.google.common.net.HostAndPort import com.google.common.util.concurrent.* import com.typesafe.config.Config @@ -15,6 +16,7 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.NodeInfo import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType +import net.corda.core.utilities.ProcessUtilities import net.corda.core.utilities.loggerFor import net.corda.node.LOGS_DIRECTORY_NAME import net.corda.node.services.config.ConfigHelper @@ -552,78 +554,53 @@ class DriverDSL( fun pickA(array: Array): A = array[Math.abs(Random().nextInt()) % array.size] private fun startNode( - executorService: ScheduledExecutorService, + executorService: ListeningScheduledExecutorService, nodeConf: FullNodeConfiguration, quasarJarPath: String, debugPort: Int?, overriddenSystemProperties: Map ): ListenableFuture { - // Write node.conf - writeConfig(nodeConf.baseDirectory, "node.conf", nodeConf.config) + return executorService.submit { + // Write node.conf + writeConfig(nodeConf.baseDirectory, "node.conf", nodeConf.config) - val className = "net.corda.node.Corda" // cannot directly get class for this, so just use string - val separator = System.getProperty("file.separator") - val classpath = System.getProperty("java.class.path") - val path = System.getProperty("java.home") + separator + "bin" + separator + "java" + val systemProperties = mapOf( + "name" to nodeConf.myLegalName, + "visualvm.display.name" to "Corda" + ) + overriddenSystemProperties + val extraJvmArguments = systemProperties.map { "-D${it.key}=${it.value}" } + + "-javaagent:$quasarJarPath" + val loggingLevel = if (debugPort == null) "INFO" else "DEBUG" - val debugPortArg = if (debugPort != null) - "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort" - else - "" - - val systemProperties = mapOf( - "name" to nodeConf.myLegalName, - "visualvm.display.name" to "Corda" - ) + overriddenSystemProperties - - val loggingLevel = if (debugPort == null) "INFO" else "DEBUG" - val javaArgs = listOf(path) + - systemProperties.map { "-D${it.key}=${it.value}" } + - listOf( - "-javaagent:$quasarJarPath", - debugPortArg, - "-Xmx200m", - "-XX:+UseG1GC", - "-cp", classpath, className, - "--base-directory=${nodeConf.baseDirectory}", - "--logging-level=$loggingLevel", - "--no-local-shell" - ).filter(String::isNotEmpty) - val process = ProcessBuilder(javaArgs) - .redirectError((nodeConf.baseDirectory / LOGS_DIRECTORY_NAME / "error.log").toFile()) - .inheritIO() - .directory(nodeConf.baseDirectory.toFile()) - .start() - // TODO There is a race condition here. Even though the messaging address is bound it may be the case that - // the handlers for the advertised services are not yet registered. Needs rethinking. - return addressMustBeBound(executorService, nodeConf.p2pAddress).map { process } + ProcessUtilities.startJavaProcess( + className = "net.corda.node.Corda", // cannot directly get class for this, so just use string + arguments = listOf( + "--base-directory=${nodeConf.baseDirectory}", + "--logging-level=$loggingLevel", + "--no-local-shell" + ), + extraJvmArguments = extraJvmArguments, + errorLogPath = nodeConf.baseDirectory / LOGS_DIRECTORY_NAME / "error.log", + workingDirectory = nodeConf.baseDirectory + ) + }.flatMap { process -> addressMustBeBound(executorService, nodeConf.p2pAddress).map { process } } } private fun startWebserver( - executorService: ScheduledExecutorService, + executorService: ListeningScheduledExecutorService, nodeConf: FullNodeConfiguration, - debugPort: Int?): ListenableFuture { - val className = "net.corda.webserver.WebServer" // cannot directly get class for this, so just use string - val separator = System.getProperty("file.separator") - val classpath = System.getProperty("java.class.path") - val path = System.getProperty("java.home") + separator + "bin" + separator + "java" - - val debugPortArg = if (debugPort != null) - listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort") - else - emptyList() - - val javaArgs = listOf(path) + - listOf("-Dname=node-${nodeConf.p2pAddress}-webserver") + debugPortArg + - listOf( - "-cp", classpath, className, - "--base-directory", nodeConf.baseDirectory.toString()) - val builder = ProcessBuilder(javaArgs) - builder.redirectError(Paths.get("error.$className.log").toFile()) - builder.inheritIO() - builder.directory(nodeConf.baseDirectory.toFile()) - val process = builder.start() - return addressMustBeBound(executorService, nodeConf.webAddress).map { process } + debugPort: Int? + ): ListenableFuture { + return executorService.submit { + val className = "net.corda.webserver.WebServer" + ProcessUtilities.startJavaProcess( + className = className, // cannot directly get class for this, so just use string + arguments = listOf("--base-directory", nodeConf.baseDirectory.toString()), + jdwpPort = debugPort, + extraJvmArguments = listOf("-Dname=node-${nodeConf.p2pAddress}-webserver"), + errorLogPath = Paths.get("error.$className.log") + ) + }.flatMap { process -> addressMustBeBound(executorService, nodeConf.webAddress).map { process } } } } } diff --git a/test-utils/src/main/kotlin/net/corda/testing/JVMSpawner.kt b/test-utils/src/main/kotlin/net/corda/testing/JVMSpawner.kt deleted file mode 100644 index 7a181049ab..0000000000 --- a/test-utils/src/main/kotlin/net/corda/testing/JVMSpawner.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.corda.testing - -import java.nio.file.Paths -import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals - -fun spawn(className: String, args: List, appName: String): Process { - val separator = System.getProperty("file.separator") - val classpath = System.getProperty("java.class.path") - val path = System.getProperty("java.home") + separator + "bin" + separator + "java" - val javaArgs = listOf(path, "-Dname=$appName", "-javaagent:lib/quasar.jar", "-cp", classpath, className) - val builder = ProcessBuilder(javaArgs + args) - builder.redirectError(Paths.get("error.$className.log").toFile()) - builder.inheritIO() - val process = builder.start() - return process -} - -fun assertExitOrKill(proc: Process) { - try { - assertEquals(proc.waitFor(2, TimeUnit.MINUTES), true) - } catch (e: Throwable) { - proc.destroyForcibly() - throw e - } -} - -fun assertAliveAndKill(proc: Process) { - try { - assertEquals(proc.isAlive, true) - } finally { - proc.destroyForcibly() - } -} diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index 5c73c848ca..d8383ebd83 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -11,6 +11,7 @@ import net.corda.core.div import net.corda.core.map import net.corda.core.random63BitValue import net.corda.core.transactions.LedgerTransaction +import net.corda.core.utilities.ProcessUtilities import net.corda.core.utilities.loggerFor import net.corda.node.driver.* import net.corda.node.services.config.configureDevKeyAndTrustStores @@ -240,35 +241,16 @@ data class VerifierDriverDSL( override fun startVerifier(address: HostAndPort): ListenableFuture { log.info("Starting verifier connecting to address $address") val id = verifierCount.andIncrement - val verifierName = "verifier$id" - val baseDirectory = driverDSL.driverDirectory / verifierName - val config = createConfiguration(baseDirectory, address) - val configFilename = "verifier.conf" - writeConfig(baseDirectory, configFilename, config) - Verifier.loadConfiguration(baseDirectory, baseDirectory / configFilename).configureDevKeyAndTrustStores(verifierName) - - val className = Verifier::class.java.name - val separator = System.getProperty("file.separator") - val classpath = System.getProperty("java.class.path") - val path = System.getProperty("java.home") + separator + "bin" + separator + "java" - val debugPortArg = if (driverDSL.isDebug) - listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${driverDSL.debugPortAllocation.nextPort()}") - else - emptyList() - - val javaArgs = - listOf(path) + debugPortArg + - listOf( - "-Xmx200m", - "-XX:+UseG1GC", - "-cp", classpath, - className, - baseDirectory.toString() - ) - val builder = ProcessBuilder(javaArgs) - builder.inheritIO() - - val processFuture = driverDSL.executorService.submit { builder.start() } + val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null + val processFuture = driverDSL.executorService.submit { + val verifierName = "verifier$id" + val baseDirectory = driverDSL.driverDirectory / verifierName + val config = createConfiguration(baseDirectory, address) + val configFilename = "verifier.conf" + writeConfig(baseDirectory, configFilename, config) + Verifier.loadConfiguration(baseDirectory, baseDirectory / configFilename).configureDevKeyAndTrustStores(verifierName) + ProcessUtilities.startJavaProcess(listOf(baseDirectory.toString()), jdwpPort = jdwpPort) + } driverDSL.shutdownManager.registerProcessShutdown(processFuture) return processFuture.map(::VerifierHandle) }