ENT-1967: Illustration for Byteman library can be used in Node integration test. (#1204) - c396b80a

(Only took `DriverDSLImpl` changes)

Simplifying internal startNode with bytemanPort parameter - d88b02f7

Manual cherry pick of these changes (c396b80a + d88b02f7)
This commit is contained in:
LankyDan 2019-11-01 07:54:18 +00:00
parent 8a2f4478d2
commit bedfba8c3d

View File

@ -18,6 +18,7 @@ import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.Try
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.millis
@ -57,6 +58,7 @@ import rx.schedulers.Schedulers
import java.io.File
import java.net.ConnectException
import java.net.URL
import java.net.URLClassLoader
import java.nio.file.Path
import java.security.cert.X509Certificate
import java.time.Duration
@ -122,7 +124,15 @@ class DriverDSLImpl(
private val state = ThreadBox(State())
//TODO: remove this once we can bundle quasar properly.
private val quasarJarPath: String by lazy { resolveJar("co.paralleluniverse.fibers.Suspendable") }
private val quasarJarPath: String by lazy { resolveJar(".*quasar.*\\.jar$").getOrThrow() }
private val bytemanJarPath: String? by lazy {
val maybeResolvedJar = resolveJar(".*byteman-\\d.*\\.jar$")
when (maybeResolvedJar) {
is Try.Success -> maybeResolvedJar.getOrThrow()
is Try.Failure -> null
}
}
private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run {
if (inMemoryDB && corda.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")) {
@ -134,14 +144,16 @@ class DriverDSLImpl(
}
}
private fun resolveJar(className: String): String {
private fun resolveJar(jarNamePattern: String): Try<String> {
return try {
val type = Class.forName(className)
val src = type.protectionDomain.codeSource
src.location.toPath().toString()
val cl = ClassLoader.getSystemClassLoader()
val urls = (cl as URLClassLoader).urLs
val jarPattern = jarNamePattern.toRegex()
val jarFileUrl = urls.first { jarPattern.matches(it.path) }
Try.Success(jarFileUrl.toPath().toString())
} catch (e: Exception) {
log.warn("Unable to locate JAR for class given by `$className` on classpath: ${e.message}", e)
throw e
log.warn("Unable to locate JAR `$jarNamePattern` on classpath: ${e.message}", e)
Try.Failure(e)
}
}
@ -178,7 +190,9 @@ class DriverDSLImpl(
}
}
override fun startNode(parameters: NodeParameters): CordaFuture<NodeHandle> {
override fun startNode(parameters: NodeParameters): CordaFuture<NodeHandle> = startNode(parameters, bytemanPort = null)
override fun startNode(parameters: NodeParameters, bytemanPort: Int?): CordaFuture<NodeHandle> {
val p2pAddress = portAllocation.nextHostAndPort()
// TODO: Derive name from the full picked name, don't just wrap the common name
val name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB")
@ -193,7 +207,7 @@ class DriverDSLImpl(
return registrationFuture.flatMap {
networkMapAvailability.flatMap {
// But starting the node proper does require the network map
startRegisteredNode(name, it, parameters, p2pAddress)
startRegisteredNode(name, it, parameters, p2pAddress, bytemanPort)
}
}
}
@ -201,7 +215,8 @@ class DriverDSLImpl(
private fun startRegisteredNode(name: CordaX500Name,
localNetworkMap: LocalNetworkMap?,
parameters: NodeParameters,
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort()): CordaFuture<NodeHandle> {
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
bytemanPort: Int? = null): CordaFuture<NodeHandle> {
val rpcAddress = portAllocation.nextHostAndPort()
val rpcAdminAddress = portAllocation.nextHostAndPort()
val webAddress = portAllocation.nextHostAndPort()
@ -240,7 +255,7 @@ class DriverDSLImpl(
allowMissingConfig = true,
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
)).checkAndOverrideForInMemoryDB()
return startNodeInternal(config, webAddress, localNetworkMap, parameters)
return startNodeInternal(config, webAddress, localNetworkMap, parameters, bytemanPort)
}
private fun startNodeRegistration(
@ -542,6 +557,8 @@ class DriverDSLImpl(
config,
quasarJarPath,
debugPort,
bytemanJarPath,
null,
systemProperties,
"512m",
null,
@ -556,7 +573,8 @@ class DriverDSLImpl(
private fun startNodeInternal(config: NodeConfig,
webAddress: NetworkHostAndPort,
localNetworkMap: LocalNetworkMap?,
parameters: NodeParameters): CordaFuture<NodeHandle> {
parameters: NodeParameters,
bytemanPort: Int?): CordaFuture<NodeHandle> {
val visibilityHandle = networkVisibilityController.register(config.corda.myLegalName)
val baseDirectory = config.corda.baseDirectory.createDirectories()
localNetworkMap?.networkParametersCopier?.install(baseDirectory)
@ -602,7 +620,16 @@ class DriverDSLImpl(
nodeFuture
} else {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, systemProperties, parameters.maximumHeapSize, parameters.logLevelOverride)
val process = startOutOfProcessNode(
config,
quasarJarPath,
debugPort,
bytemanJarPath,
bytemanPort,
systemProperties,
parameters.maximumHeapSize,
parameters.logLevelOverride
)
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
// true because we don't want orphaned processes in the case that the parent process is terminated by the
@ -730,12 +757,16 @@ class DriverDSLImpl(
config: NodeConfig,
quasarJarPath: String,
debugPort: Int?,
bytemanJarPath: String?,
bytemanPort: Int?,
overriddenSystemProperties: Map<String, String>,
maximumHeapSize: String,
logLevelOverride: String?,
vararg extraCmdLineFlag: String
): Process {
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled"))
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
"debug port is " + (debugPort ?: "not enabled") + ", " +
"byteMan: " + if (bytemanJarPath == null) "not in classpath" else "port is " + (bytemanPort ?: "not enabled"))
// Write node.conf
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
@ -777,6 +808,17 @@ class DriverDSLImpl(
it += extraCmdLineFlag
}.toList()
val bytemanJvmArgs = {
val bytemanAgent = bytemanJarPath?.let {
bytemanPort?.let {
"-javaagent:$bytemanJarPath=port:$bytemanPort,listener:true"
}
}
listOfNotNull(bytemanAgent) +
if (bytemanAgent != null && debugPort != null) listOf("-Dorg.jboss.byteman.verbose=true", "-Dorg.jboss.byteman.debug=true")
else emptyList()
}.invoke()
// The following dependencies are excluded from the classpath of the created JVM, so that the environment resembles a real one as close as possible.
// These are either classes that will be added as attachments to the node (i.e. samples, finance, opengamma etc.) or irrelevant testing libraries (test, corda-mock etc.).
// TODO: There is pending work to fix this issue without custom blacklisting. See: https://r3-cev.atlassian.net/browse/CORDA-2164.
@ -789,7 +831,7 @@ class DriverDSLImpl(
className = "net.corda.node.Corda", // cannot directly get class for this, so just use string
arguments = arguments,
jdwpPort = debugPort,
extraJvmArguments = extraJvmArguments,
extraJvmArguments = extraJvmArguments + bytemanJvmArgs,
workingDirectory = config.corda.baseDirectory,
maximumHeapSize = maximumHeapSize,
classPath = cp
@ -952,6 +994,11 @@ interface InternalDriverDSL : DriverDSL {
fun start()
fun shutdown()
fun startNode(
parameters: NodeParameters = NodeParameters(),
bytemanPort: Int? = null
): CordaFuture<NodeHandle>
}
/**