mirror of
https://github.com/corda/corda.git
synced 2025-02-20 17:33:15 +00:00
CORDA-2676: Allow more Network Bootstrapper code to be unloaded from JVM.
This commit is contained in:
parent
fa2cd907c5
commit
8306b3f708
@ -29,7 +29,7 @@ import net.corda.serialization.internal.SerializationFactoryImpl
|
||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||
import net.corda.serialization.internal.amqp.amqpMagic
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||
@ -53,14 +53,14 @@ import kotlin.streams.toList
|
||||
class NetworkBootstrapper
|
||||
@VisibleForTesting
|
||||
internal constructor(private val initSerEnv: Boolean,
|
||||
private val embeddedCordaJar: () -> InputStream,
|
||||
private val embeddedCordaJar: () -> URL,
|
||||
private val nodeInfosGenerator: (List<Path>) -> List<Path>,
|
||||
private val contractsJarConverter: (Path) -> ContractsJar) : NetworkBootstrapperWithOverridableParameters {
|
||||
|
||||
constructor() : this(
|
||||
initSerEnv = true,
|
||||
embeddedCordaJar = Companion::extractEmbeddedCordaJar,
|
||||
nodeInfosGenerator = Companion::generateNodeInfos,
|
||||
embeddedCordaJar = ::extractEmbeddedCordaJar,
|
||||
nodeInfosGenerator = ::generateNodeInfos,
|
||||
contractsJarConverter = ::ContractsJarFile
|
||||
)
|
||||
|
||||
@ -77,8 +77,8 @@ internal constructor(private val initSerEnv: Boolean,
|
||||
|
||||
private val jarsThatArentCordapps = setOf("corda.jar", "runnodes.jar")
|
||||
|
||||
private fun extractEmbeddedCordaJar(): InputStream {
|
||||
return Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar")
|
||||
private fun extractEmbeddedCordaJar(): URL {
|
||||
return Thread.currentThread().contextClassLoader.getResource("corda.jar")
|
||||
}
|
||||
|
||||
private fun generateNodeInfos(nodeDirs: List<Path>): List<Path> {
|
||||
@ -106,13 +106,19 @@ internal constructor(private val initSerEnv: Boolean,
|
||||
.redirectOutput(nodeInfoGenFile)
|
||||
.apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" }
|
||||
.start()
|
||||
if (!process.waitFor(3, TimeUnit.MINUTES)) {
|
||||
try {
|
||||
if (!process.waitFor(3, TimeUnit.MINUTES)) {
|
||||
process.destroyForcibly()
|
||||
printNodeInfoGenLogToConsole(nodeInfoGenFile)
|
||||
}
|
||||
printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 }
|
||||
return nodeDir.list { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
// Don't leave this process dangling if the thread is interrupted.
|
||||
process.destroyForcibly()
|
||||
printNodeInfoGenLogToConsole(nodeInfoGenFile)
|
||||
}
|
||||
printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 }
|
||||
return nodeDir.list { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,19 +263,23 @@ internal constructor(private val initSerEnv: Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
private fun Path.listEndingWith(suffix: String): List<Path> {
|
||||
return list { file -> file.filter { it.toString().endsWith(suffix) }.toList() }
|
||||
}
|
||||
|
||||
private fun createNodeDirectoriesIfNeeded(directory: Path, fromCordform: Boolean): Boolean {
|
||||
var networkAlreadyExists = false
|
||||
val cordaJar = directory / "corda.jar"
|
||||
var usingEmbedded = false
|
||||
if (!cordaJar.exists()) {
|
||||
embeddedCordaJar().use { it.copyTo(cordaJar) }
|
||||
embeddedCordaJar().openStream().use { it.copyTo(cordaJar) }
|
||||
usingEmbedded = true
|
||||
} else if (!fromCordform) {
|
||||
println("Using corda.jar in root directory")
|
||||
}
|
||||
|
||||
val confFiles = directory.list { it.filter { it.toString().endsWith("_node.conf") }.toList() }
|
||||
val webServerConfFiles = directory.list { it.filter { it.toString().endsWith("_web-server.conf") }.toList() }
|
||||
val confFiles = directory.listEndingWith("_node.conf")
|
||||
val webServerConfFiles = directory.listEndingWith("_web-server.conf")
|
||||
|
||||
for (confFile in confFiles) {
|
||||
val nodeName = confFile.fileName.toString().removeSuffix("_node.conf")
|
||||
@ -285,11 +295,10 @@ internal constructor(private val initSerEnv: Boolean,
|
||||
cordaJar.copyToDirectory(nodeDir, REPLACE_EXISTING)
|
||||
}
|
||||
|
||||
directory.list { paths ->
|
||||
paths.filter { (it / "node.conf").exists() && !(it / "corda.jar").exists() }.forEach {
|
||||
println("Copying corda.jar into node directory ${it.fileName}")
|
||||
cordaJar.copyToDirectory(it)
|
||||
}
|
||||
val nodeDirs = directory.list { subDir -> subDir.filter { (it / "node.conf").exists() && !(it / "corda.jar").exists() }.toList() }
|
||||
for (nodeDir in nodeDirs) {
|
||||
println("Copying corda.jar into node directory ${nodeDir.fileName}")
|
||||
cordaJar.copyToDirectory(nodeDir)
|
||||
}
|
||||
|
||||
if (fromCordform) {
|
||||
@ -304,15 +313,11 @@ internal constructor(private val initSerEnv: Boolean,
|
||||
}
|
||||
|
||||
private fun gatherNodeDirectories(directory: Path): List<Path> {
|
||||
return directory.list { paths ->
|
||||
paths.filter {
|
||||
val exists = (it / "corda.jar").exists()
|
||||
if (exists) {
|
||||
require((it / "node.conf").exists()) { "Missing node.conf in node directory ${it.fileName}" }
|
||||
}
|
||||
exists
|
||||
}.toList()
|
||||
val nodeDirs = directory.list { subDir -> subDir.filter { (it / "corda.jar").exists() }.toList() }
|
||||
for (nodeDir in nodeDirs) {
|
||||
require((nodeDir / "node.conf").exists()) { "Missing node.conf in node directory ${nodeDir.fileName}" }
|
||||
}
|
||||
return nodeDirs
|
||||
}
|
||||
|
||||
private fun distributeNodeInfos(nodeDirs: List<Path>, nodeInfoFiles: List<Path>) {
|
||||
@ -434,13 +439,13 @@ internal constructor(private val initSerEnv: Boolean,
|
||||
private fun initialiseSerialization() {
|
||||
_contextSerializationEnv.set(SerializationEnvironment.with(
|
||||
SerializationFactoryImpl().apply {
|
||||
registerScheme(AMQPParametersSerializationScheme)
|
||||
registerScheme(AMQPParametersSerializationScheme())
|
||||
},
|
||||
AMQP_P2P_CONTEXT)
|
||||
)
|
||||
}
|
||||
|
||||
private object AMQPParametersSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) {
|
||||
private class AMQPParametersSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) {
|
||||
override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
|
||||
override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
|
||||
|
||||
@ -526,7 +531,7 @@ enum class CopyCordapps {
|
||||
|
||||
fun copy(cordappJars: List<Path>, nodeDirs: List<Path>, networkAlreadyExists: Boolean, fromCordform: Boolean) {
|
||||
if (!fromCordform) {
|
||||
println("Found the following CorDapps: ${cordappJars.map { it.fileName }}")
|
||||
println("Found the following CorDapps: ${cordappJars.map(Path::getFileName)}")
|
||||
}
|
||||
this.copyTo(cordappJars, nodeDirs, networkAlreadyExists, fromCordform)
|
||||
}
|
||||
|
@ -23,10 +23,12 @@ import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.ExpectedException
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.security.PublicKey
|
||||
import java.time.Duration
|
||||
@ -45,13 +47,28 @@ class NetworkBootstrapperTest {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
private val fakeEmbeddedCordaJar = fakeFileBytes()
|
||||
companion object {
|
||||
private val fakeEmbeddedCorda = fakeFileBytes()
|
||||
private val fakeEmbeddedCordaJar = Files.createTempFile("corda", ".jar").write(fakeEmbeddedCorda)
|
||||
|
||||
private val contractsJars = HashMap<Path, TestContractsJar>()
|
||||
private fun fakeFileBytes(writeToFile: Path? = null): ByteArray {
|
||||
val bytes = secureRandomBytes(128)
|
||||
writeToFile?.write(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@AfterClass
|
||||
fun cleanUp() {
|
||||
Files.delete(fakeEmbeddedCordaJar)
|
||||
}
|
||||
}
|
||||
|
||||
private val contractsJars = hashMapOf<Path, TestContractsJar>()
|
||||
|
||||
private val bootstrapper = NetworkBootstrapper(
|
||||
initSerEnv = false,
|
||||
embeddedCordaJar = fakeEmbeddedCordaJar::inputStream,
|
||||
embeddedCordaJar = { fakeEmbeddedCordaJar.toUri().toURL() },
|
||||
nodeInfosGenerator = { nodeDirs ->
|
||||
nodeDirs.map { nodeDir ->
|
||||
val name = nodeDir.fakeNodeConfig.myLegalName
|
||||
@ -101,7 +118,7 @@ class NetworkBootstrapperTest {
|
||||
fun `single node conf file`() {
|
||||
createNodeConfFile("node1", bobConfig)
|
||||
bootstrap()
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "node1" to bobConfig)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "node1" to bobConfig)
|
||||
networkParameters.run {
|
||||
assertThat(epoch).isEqualTo(1)
|
||||
assertThat(notaries).isEmpty()
|
||||
@ -121,7 +138,7 @@ class NetworkBootstrapperTest {
|
||||
fun `single node directory with just node conf file`() {
|
||||
createNodeDir("bob", bobConfig)
|
||||
bootstrap()
|
||||
assertBootstrappedNetwork(fakeEmbeddedCordaJar, "bob" to bobConfig)
|
||||
assertBootstrappedNetwork(fakeEmbeddedCorda, "bob" to bobConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -147,7 +164,7 @@ class NetworkBootstrapperTest {
|
||||
createNodeConfFile("alice", aliceConfig)
|
||||
createNodeConfFile("notary", notaryConfig)
|
||||
bootstrap()
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "notary" to notaryConfig)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "notary" to notaryConfig)
|
||||
networkParameters.assertContainsNotary("notary")
|
||||
}
|
||||
|
||||
@ -165,7 +182,7 @@ class NetworkBootstrapperTest {
|
||||
createNodeConfFile("alice", aliceConfig)
|
||||
createNodeDir("bob", bobConfig)
|
||||
bootstrap()
|
||||
assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "bob" to bobConfig)
|
||||
assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "bob" to bobConfig)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -173,7 +190,7 @@ class NetworkBootstrapperTest {
|
||||
createNodeConfFile("alice", aliceConfig)
|
||||
val cordappBytes = createFakeCordappJar("sample-app", listOf("contract.class"))
|
||||
bootstrap()
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig)
|
||||
assertThat(rootDir / "alice" / "cordapps" / "sample-app.jar").hasBinaryContent(cordappBytes)
|
||||
assertThat(networkParameters.whitelistedContractImplementations).isEqualTo(mapOf(
|
||||
"contract.class" to listOf(cordappBytes.sha256())
|
||||
@ -185,7 +202,7 @@ class NetworkBootstrapperTest {
|
||||
createNodeConfFile("alice", aliceConfig)
|
||||
val cordappBytes = createFakeCordappJar("sample-app", listOf("contract.class"))
|
||||
bootstrap(copyCordapps = CopyCordapps.No)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig)
|
||||
assertThat(rootDir / "alice" / "cordapps" / "sample-app.jar").doesNotExist()
|
||||
assertThat(networkParameters.whitelistedContractImplementations).isEqualTo(mapOf(
|
||||
"contract.class" to listOf(cordappBytes.sha256())
|
||||
@ -199,7 +216,7 @@ class NetworkBootstrapperTest {
|
||||
val networkParameters1 = (rootDir / "alice").networkParameters
|
||||
createNodeConfFile("bob", bobConfig)
|
||||
bootstrap()
|
||||
val networkParameters2 = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "bob" to bobConfig)
|
||||
val networkParameters2 = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "bob" to bobConfig)
|
||||
assertThat(networkParameters1).isEqualTo(networkParameters2)
|
||||
}
|
||||
|
||||
@ -209,7 +226,7 @@ class NetworkBootstrapperTest {
|
||||
bootstrap()
|
||||
createNodeConfFile("notary", notaryConfig)
|
||||
bootstrap()
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig, "notary" to notaryConfig)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig, "notary" to notaryConfig)
|
||||
networkParameters.assertContainsNotary("notary")
|
||||
assertThat(networkParameters.epoch).isEqualTo(2)
|
||||
}
|
||||
@ -225,7 +242,7 @@ class NetworkBootstrapperTest {
|
||||
maxMessageSize = maxMessageSize,
|
||||
maxTransactionSize = maxTransactionSize,
|
||||
eventHorizon = eventHorizon)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig)
|
||||
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCorda, "alice" to aliceConfig)
|
||||
assertThat(networkParameters.minimumPlatformVersion).isEqualTo(minimumPlatformVersion)
|
||||
assertThat(networkParameters.maxMessageSize).isEqualTo(maxMessageSize)
|
||||
assertThat(networkParameters.maxTransactionSize).isEqualTo(maxTransactionSize)
|
||||
@ -299,12 +316,6 @@ class NetworkBootstrapperTest {
|
||||
|
||||
private val rootDir get() = tempFolder.root.toPath()
|
||||
|
||||
private fun fakeFileBytes(writeToFile: Path? = null): ByteArray {
|
||||
val bytes = secureRandomBytes(128)
|
||||
writeToFile?.write(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
private fun bootstrap(copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly,
|
||||
packageOwnership: Map<String, PublicKey>? = emptyMap(),
|
||||
minimumPlatformVerison: Int? = PLATFORM_VERSION,
|
||||
@ -317,7 +328,7 @@ class NetworkBootstrapperTest {
|
||||
maxMessageSize = maxMessageSize,
|
||||
maxTransactionSize = maxTransactionSize,
|
||||
eventHorizon = eventHorizon,
|
||||
packageOwnership = packageOwnership?.map { PackageOwner(it.key, it.value!!) }
|
||||
packageOwnership = packageOwnership?.map { PackageOwner(it.key, it.value) }
|
||||
))
|
||||
}
|
||||
|
||||
@ -364,7 +375,7 @@ class NetworkBootstrapperTest {
|
||||
|
||||
private fun assertBootstrappedNetwork(cordaJar: ByteArray, vararg nodes: Pair<String, FakeNodeConfig>): NetworkParameters {
|
||||
val networkParameters = (rootDir / nodes[0].first).networkParameters
|
||||
val allNodeInfoFiles = nodes.map { (rootDir / it.first).nodeInfoFile }.associateBy({ it }, { it.readAll() })
|
||||
val allNodeInfoFiles = nodes.map { (rootDir / it.first).nodeInfoFile }.associateBy({ it }, Path::readAll)
|
||||
|
||||
for ((nodeDirName, config) in nodes) {
|
||||
val nodeDir = rootDir / nodeDirName
|
||||
|
Loading…
x
Reference in New Issue
Block a user