#340: Factor out jvm process start into utility function

This commit is contained in:
exfalso 2017-03-27 15:36:56 +01:00
parent 48952dfc02
commit 81dcde99bf
4 changed files with 96 additions and 124 deletions

View File

@ -0,0 +1,47 @@
package net.corda.core.utilities
import java.nio.file.Path
object ProcessUtilities {
inline fun <reified C : Any> startJavaProcess(
arguments: List<String>,
jdwpPort: Int? = null,
extraJvmArguments: List<String> = 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<String>,
jdwpPort: Int? = null,
extraJvmArguments: List<String> = 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()
}
}

View File

@ -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 <A> pickA(array: Array<A>): A = array[Math.abs(Random().nextInt()) % array.size]
private fun startNode(
executorService: ScheduledExecutorService,
executorService: ListeningScheduledExecutorService,
nodeConf: FullNodeConfiguration,
quasarJarPath: String,
debugPort: Int?,
overriddenSystemProperties: Map<String, String>
): ListenableFuture<Process> {
// Write node.conf
writeConfig(nodeConf.baseDirectory, "node.conf", nodeConf.config)
return executorService.submit<Process> {
// 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<Process> {
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<Process> {
return executorService.submit<Process> {
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 } }
}
}
}

View File

@ -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<String>, 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()
}
}

View File

@ -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<VerifierHandle> {
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<Process> { builder.start() }
val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null
val processFuture = driverDSL.executorService.submit<Process> {
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<Verifier>(listOf(baseDirectory.toString()), jdwpPort = jdwpPort)
}
driverDSL.shutdownManager.registerProcessShutdown(processFuture)
return processFuture.map(::VerifierHandle)
}