mirror of
https://github.com/corda/corda.git
synced 2025-06-21 16:49:45 +00:00
[CORDA-1799]: Avoid generating test CorDapp JARs from each out of process node started by the driver (#3641)
This commit is contained in:
committed by
GitHub
parent
e7f3847839
commit
abc1d99eaa
@ -20,10 +20,7 @@ import net.corda.testing.driver.PortAllocation.Incremental
|
||||
import net.corda.testing.driver.internal.internalServices
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl
|
||||
import net.corda.testing.node.internal.genericDriver
|
||||
import net.corda.testing.node.internal.getTimestampAsDirectoryName
|
||||
import net.corda.testing.node.internal.newContext
|
||||
import net.corda.testing.node.internal.*
|
||||
import rx.Observable
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
@ -136,6 +133,9 @@ abstract class PortAllocation {
|
||||
* @property startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @property maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
* @property logLevel Logging level threshold.
|
||||
* @property additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||
* @property regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class NodeParameters(
|
||||
@ -145,8 +145,57 @@ data class NodeParameters(
|
||||
val customOverrides: Map<String, Any?> = emptyMap(),
|
||||
val startInSameProcess: Boolean? = null,
|
||||
val maximumHeapSize: String = "512m",
|
||||
val logLevel: String? = null
|
||||
val logLevel: String? = null,
|
||||
val additionalCordapps: Set<TestCorDapp> = emptySet(),
|
||||
val regenerateCordappsOnStart: Boolean = false
|
||||
) {
|
||||
/**
|
||||
* Helper builder for configuring a [Node] from Java.
|
||||
*
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to a single user with
|
||||
* all permissions.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
* @param logLevel Logging level threshold.
|
||||
*/
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
logLevel: String? = null
|
||||
) : this(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
logLevel,
|
||||
additionalCordapps = emptySet(),
|
||||
regenerateCordappsOnStart = false
|
||||
)
|
||||
|
||||
/**
|
||||
* Helper builder for configuring a [Node] from Java.
|
||||
*
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to a single user with
|
||||
* all permissions.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
*/
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
@ -161,7 +210,44 @@ data class NodeParameters(
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
null)
|
||||
null,
|
||||
additionalCordapps = emptySet(),
|
||||
regenerateCordappsOnStart = false)
|
||||
|
||||
/**
|
||||
* Helper builder for configuring a [Node] from Java.
|
||||
*
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to a single user with
|
||||
* all permissions.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||
* @param regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||
*/
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Set<TestCorDapp> = emptySet(),
|
||||
regenerateCordappsOnStart: Boolean = false
|
||||
) : this(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
null,
|
||||
additionalCordapps,
|
||||
regenerateCordappsOnStart)
|
||||
|
||||
fun copy(
|
||||
providedName: CordaX500Name?,
|
||||
@ -179,6 +265,25 @@ data class NodeParameters(
|
||||
maximumHeapSize,
|
||||
null)
|
||||
|
||||
fun copy(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
logLevel: String?
|
||||
) = this.copy(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
logLevel,
|
||||
additionalCordapps = additionalCordapps,
|
||||
regenerateCordappsOnStart = regenerateCordappsOnStart)
|
||||
|
||||
fun withProvidedName(providedName: CordaX500Name?): NodeParameters = copy(providedName = providedName)
|
||||
fun withRpcUsers(rpcUsers: List<User>): NodeParameters = copy(rpcUsers = rpcUsers)
|
||||
fun withVerifierType(verifierType: VerifierType): NodeParameters = copy(verifierType = verifierType)
|
||||
@ -186,6 +291,8 @@ data class NodeParameters(
|
||||
fun withStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess)
|
||||
fun withMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize)
|
||||
fun withLogLevel(logLevel: String?): NodeParameters = copy(logLevel = logLevel)
|
||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCorDapp>): NodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||
fun withDeleteExistingCordappsDirectory(regenerateCordappsOnStart: Boolean): NodeParameters = copy(regenerateCordappsOnStart = regenerateCordappsOnStart)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,12 +337,12 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
||||
startNodesInProcess = defaultParameters.startNodesInProcess,
|
||||
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
|
||||
notarySpecs = defaultParameters.notarySpecs,
|
||||
extraCordappPackagesToScan = defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy = defaultParameters.jmxPolicy,
|
||||
compatibilityZone = null,
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = defaultParameters.cordappsForAllNodes()
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -272,6 +379,7 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
||||
* @property inMemoryDB Whether to use in-memory H2 for new nodes rather then on-disk (the node starts quicker, however
|
||||
* the data is not persisted between node restarts). Has no effect if node is configured
|
||||
* in any way to use database other than H2.
|
||||
* @property cordappsForAllNodes [TestCorDapp]s that will be added to each node started by the [DriverDSL].
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class DriverParameters(
|
||||
@ -289,8 +397,44 @@ data class DriverParameters(
|
||||
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
|
||||
val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
val initialiseSerialization: Boolean = true,
|
||||
val inMemoryDB: Boolean = true
|
||||
val inMemoryDB: Boolean = true,
|
||||
val cordappsForAllNodes: Set<TestCorDapp>? = null
|
||||
) {
|
||||
constructor(
|
||||
isDebug: Boolean = false,
|
||||
driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||
portAllocation: PortAllocation = PortAllocation.Incremental(10000),
|
||||
debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
|
||||
systemProperties: Map<String, String> = emptyMap(),
|
||||
useTestClock: Boolean = false,
|
||||
startNodesInProcess: Boolean = false,
|
||||
waitForAllNodesToFinish: Boolean = false,
|
||||
notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY_NAME)),
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
|
||||
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
initialiseSerialization: Boolean = true,
|
||||
inMemoryDB: Boolean = true
|
||||
) : this(
|
||||
isDebug,
|
||||
driverDirectory,
|
||||
portAllocation,
|
||||
debugPortAllocation,
|
||||
systemProperties,
|
||||
useTestClock,
|
||||
startNodesInProcess,
|
||||
waitForAllNodesToFinish,
|
||||
notarySpecs,
|
||||
extraCordappPackagesToScan,
|
||||
jmxPolicy,
|
||||
networkParameters,
|
||||
notaryCustomOverrides,
|
||||
initialiseSerialization,
|
||||
inMemoryDB,
|
||||
cordappsForAllNodes = null
|
||||
)
|
||||
|
||||
constructor(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
@ -319,7 +463,41 @@ data class DriverParameters(
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
true,
|
||||
true
|
||||
true,
|
||||
cordappsForAllNodes = null
|
||||
)
|
||||
|
||||
constructor(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
cordappsForAllNodes: Set<TestCorDapp>? = null
|
||||
) : this(
|
||||
isDebug,
|
||||
driverDirectory,
|
||||
portAllocation,
|
||||
debugPortAllocation,
|
||||
systemProperties,
|
||||
useTestClock,
|
||||
startNodesInProcess,
|
||||
waitForAllNodesToFinish,
|
||||
notarySpecs,
|
||||
extraCordappPackagesToScan,
|
||||
jmxPolicy,
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
true,
|
||||
true,
|
||||
cordappsForAllNodes
|
||||
)
|
||||
|
||||
constructor(
|
||||
@ -352,7 +530,43 @@ data class DriverParameters(
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
initialiseSerialization,
|
||||
inMemoryDB
|
||||
inMemoryDB,
|
||||
cordappsForAllNodes = null
|
||||
)
|
||||
|
||||
constructor(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
initialiseSerialization: Boolean,
|
||||
inMemoryDB: Boolean,
|
||||
cordappsForAllNodes: Set<TestCorDapp>? = null
|
||||
) : this(
|
||||
isDebug,
|
||||
driverDirectory,
|
||||
portAllocation,
|
||||
debugPortAllocation,
|
||||
systemProperties,
|
||||
useTestClock,
|
||||
startNodesInProcess,
|
||||
waitForAllNodesToFinish,
|
||||
notarySpecs,
|
||||
extraCordappPackagesToScan,
|
||||
jmxPolicy,
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
initialiseSerialization,
|
||||
inMemoryDB,
|
||||
cordappsForAllNodes
|
||||
)
|
||||
|
||||
fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug)
|
||||
@ -370,6 +584,7 @@ data class DriverParameters(
|
||||
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)
|
||||
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
|
||||
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
|
||||
fun withCordappsForAllNodes(cordappsForAllNodes: Set<TestCorDapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
@ -431,4 +646,69 @@ data class DriverParameters(
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
initialiseSerialization = initialiseSerialization
|
||||
)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
cordappsForAllNodes: Set<TestCorDapp>?
|
||||
) = this.copy(
|
||||
isDebug = isDebug,
|
||||
driverDirectory = driverDirectory,
|
||||
portAllocation = portAllocation,
|
||||
debugPortAllocation = debugPortAllocation,
|
||||
systemProperties = systemProperties,
|
||||
useTestClock = useTestClock,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
initialiseSerialization = true,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
initialiseSerialization: Boolean,
|
||||
cordappsForAllNodes: Set<TestCorDapp>?
|
||||
) = this.copy(
|
||||
isDebug = isDebug,
|
||||
driverDirectory = driverDirectory,
|
||||
portAllocation = portAllocation,
|
||||
debugPortAllocation = debugPortAllocation,
|
||||
systemProperties = systemProperties,
|
||||
useTestClock = useTestClock,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
initialiseSerialization = initialiseSerialization,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
)
|
||||
}
|
||||
|
@ -80,6 +80,38 @@ interface DriverDSL {
|
||||
maximumHeapSize: String = defaultParameters.maximumHeapSize
|
||||
): CordaFuture<NodeHandle>
|
||||
|
||||
/**
|
||||
* Start a node.
|
||||
*
|
||||
* @param defaultParameters The default parameters for the node. Allows the node to be configured in builder style
|
||||
* when called from Java code.
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType].
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node as a [String]. By default a number is interpreted
|
||||
* as being in bytes. Append the letter 'k' or 'K' to the value to indicate Kilobytes, 'm' or 'M' to indicate
|
||||
* megabytes, and 'g' or 'G' to indicate gigabytes. The default value is "512m" = 512 megabytes.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||
* @param regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||
* @return A [CordaFuture] on the [NodeHandle] to the node. The future will complete when the node is available and
|
||||
* it sees all previously started nodes, including the notaries.
|
||||
*/
|
||||
fun startNode(
|
||||
defaultParameters: NodeParameters = NodeParameters(),
|
||||
providedName: CordaX500Name? = defaultParameters.providedName,
|
||||
rpcUsers: List<User> = defaultParameters.rpcUsers,
|
||||
verifierType: VerifierType = defaultParameters.verifierType,
|
||||
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
||||
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
||||
maximumHeapSize: String = defaultParameters.maximumHeapSize,
|
||||
additionalCordapps: Set<TestCorDapp> = defaultParameters.additionalCordapps,
|
||||
regenerateCordappsOnStart: Boolean = defaultParameters.regenerateCordappsOnStart
|
||||
): CordaFuture<NodeHandle>
|
||||
|
||||
/**
|
||||
* Helper function for starting a [Node] with custom parameters from Java.
|
||||
*
|
||||
|
@ -0,0 +1,85 @@
|
||||
package net.corda.testing.driver
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.testing.node.internal.MutableTestCorDapp
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Represents information about a CorDapp. Used to generate CorDapp JARs in tests.
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface TestCorDapp {
|
||||
|
||||
val name: String
|
||||
val title: String
|
||||
val version: String
|
||||
val vendor: String
|
||||
|
||||
val classes: Set<Class<*>>
|
||||
|
||||
val resources: Set<URL>
|
||||
|
||||
fun packageAsJarInDirectory(parentDirectory: Path): Path
|
||||
|
||||
fun packageAsJarWithPath(jarFilePath: Path)
|
||||
|
||||
/**
|
||||
* Responsible of creating [TestCorDapp]s.
|
||||
*/
|
||||
class Factory {
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Returns a builder-style [TestCorDapp] to easily generate different [TestCorDapp]s that have something in common.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun create(name: String, version: String, vendor: String = "R3", title: String = name, classes: Set<Class<*>> = emptySet(), willResourceBeAddedBeToCorDapp: (fullyQualifiedName: String, url: URL) -> Boolean = MutableTestCorDapp.Companion::filterTestCorDappClass): TestCorDapp.Mutable {
|
||||
|
||||
return MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedBeToCorDapp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DoNotImplement
|
||||
interface Mutable : TestCorDapp {
|
||||
|
||||
fun withName(name: String): TestCorDapp.Mutable
|
||||
|
||||
fun withTitle(title: String): TestCorDapp.Mutable
|
||||
|
||||
fun withVersion(version: String): TestCorDapp.Mutable
|
||||
|
||||
fun withVendor(vendor: String): TestCorDapp.Mutable
|
||||
|
||||
fun withClasses(classes: Set<Class<*>>): TestCorDapp.Mutable
|
||||
|
||||
fun plusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
||||
|
||||
fun minusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
||||
|
||||
fun plusPackage(pckg: String): TestCorDapp.Mutable = plusPackages(setOf(pckg))
|
||||
|
||||
fun minusPackage(pckg: String): TestCorDapp.Mutable = minusPackages(setOf(pckg))
|
||||
|
||||
fun plusPackage(pckg: Package): TestCorDapp.Mutable = plusPackages(pckg.name)
|
||||
|
||||
fun minusPackage(pckg: Package): TestCorDapp.Mutable = minusPackages(pckg.name)
|
||||
|
||||
operator fun plus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes + clazz)
|
||||
|
||||
operator fun minus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes - clazz)
|
||||
|
||||
fun plusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = plusPackages(setOf(pckg, *pckgs))
|
||||
|
||||
fun plusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
||||
|
||||
fun minusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs))
|
||||
|
||||
fun minusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
||||
|
||||
fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
||||
|
||||
fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
||||
}
|
||||
}
|
@ -14,10 +14,8 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.node.internal.InternalMockMessagingService
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.newContext
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.node.internal.*
|
||||
import rx.Observable
|
||||
import java.math.BigInteger
|
||||
import java.nio.file.Path
|
||||
@ -33,22 +31,36 @@ import java.nio.file.Path
|
||||
* @property entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||
* @property configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||
* @property extraCordappPackages Extra CorDapp packages to include for this node.
|
||||
* @property additionalCordapps [TestCorDapp]s that will be added to this node in addition to the ones shared by all nodes, which get specified at [MockNetwork] level.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class MockNodeParameters @JvmOverloads constructor(
|
||||
data class MockNodeParameters constructor(
|
||||
val forcedID: Int? = null,
|
||||
val legalName: CordaX500Name? = null,
|
||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
val extraCordappPackages: List<String> = emptyList()) {
|
||||
val additionalCordapps: Set<TestCorDapp>) {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
forcedID: Int? = null,
|
||||
legalName: CordaX500Name? = null,
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
extraCordappPackages: List<String> = emptyList()
|
||||
) : this(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps = cordappsForPackages(extraCordappPackages))
|
||||
|
||||
fun withForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID)
|
||||
fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName)
|
||||
fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot)
|
||||
fun withConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides)
|
||||
fun withExtraCordappPackages(extraCordappPackages: List<String>): MockNodeParameters = copy(extraCordappPackages = extraCordappPackages)
|
||||
fun withExtraCordappPackages(extraCordappPackages: List<String>): MockNodeParameters = copy(forcedID = forcedID, legalName = legalName, entropyRoot = entropyRoot, configOverrides = configOverrides, extraCordappPackages = extraCordappPackages)
|
||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCorDapp>): MockNodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters {
|
||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides)
|
||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps = emptySet())
|
||||
}
|
||||
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?, extraCordappPackages: List<String> = emptyList()): MockNodeParameters {
|
||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,6 +217,7 @@ class StartedMockNode private constructor(private val node: StartedNode<Internal
|
||||
* @property notarySpecs The notaries to use in the mock network. By default you get one mock notary and that is usually sufficient.
|
||||
* @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be
|
||||
* empty as notaries are defined by [notarySpecs].
|
||||
* @property cordappsForAllNodes [TestCorDapp]s that will be added to each node started by the [MockNetwork].
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
open class MockNetwork(
|
||||
@ -214,11 +227,23 @@ open class MockNetwork(
|
||||
val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
val networkParameters: NetworkParameters = defaultParameters.networkParameters) {
|
||||
val networkParameters: NetworkParameters = defaultParameters.networkParameters,
|
||||
val cordappsForAllNodes: Set<TestCorDapp> = cordappsForPackages(cordappPackages)) {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
|
||||
|
||||
private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(cordappPackages, defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters)
|
||||
constructor(
|
||||
cordappPackages: List<String>,
|
||||
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
||||
threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
networkParameters: NetworkParameters = defaultParameters.networkParameters
|
||||
) : this(emptyList(), defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
|
||||
private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters = networkParameters, cordappsForAllNodes = cordappsForAllNodes)
|
||||
|
||||
/** In a mock network, nodes have an incrementing integer ID. Real networks do not have this. Returns the next ID that will be used. */
|
||||
val nextNodeId get(): Int = internalMockNetwork.nextNodeId
|
||||
@ -262,7 +287,26 @@ open class MockNetwork(
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
extraCordappPackages: List<String> = emptyList()): StartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
||||
|
||||
return createNode(legalName, forcedID, entropyRoot, configOverrides, cordappsForPackages(extraCordappPackages))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a started node with the given parameters.
|
||||
*
|
||||
* @param legalName The node's legal name.
|
||||
* @param forcedID A unique identifier for the node.
|
||||
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
||||
*/
|
||||
fun createNode(legalName: CordaX500Name? = null,
|
||||
forcedID: Int? = null,
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
additionalCordapps: Set<TestCorDapp>): StartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
||||
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
|
||||
}
|
||||
|
||||
@ -285,7 +329,26 @@ open class MockNetwork(
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
extraCordappPackages: List<String> = emptyList()): UnstartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
||||
|
||||
return createUnstartedNode(legalName, forcedID, entropyRoot, configOverrides, cordappsForPackages(extraCordappPackages))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an unstarted node with the given parameters.
|
||||
*
|
||||
* @param legalName The node's legal name.
|
||||
* @param forcedID A unique identifier for the node.
|
||||
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
||||
*/
|
||||
fun createUnstartedNode(legalName: CordaX500Name? = null,
|
||||
forcedID: Int? = null,
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
additionalCordapps: Set<TestCorDapp> = emptySet()): UnstartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
||||
return UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(InternalMockNodeParameters(parameters)))
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,10 @@ import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.ServicesForResolutionImpl
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.*
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.services.schema.HibernateObserver
|
||||
@ -36,6 +37,8 @@ import net.corda.testing.internal.DEV_ROOT_CA
|
||||
import net.corda.testing.internal.MockCordappProvider
|
||||
import net.corda.testing.node.internal.MockKeyManagementService
|
||||
import net.corda.testing.node.internal.MockTransactionStorage
|
||||
import net.corda.testing.node.internal.TestCordappDirectories
|
||||
import net.corda.testing.node.internal.getCallerPackage
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import java.security.KeyPair
|
||||
import java.sql.Connection
|
||||
@ -64,7 +67,15 @@ open class MockServices private constructor(
|
||||
private val initialIdentity: TestIdentity,
|
||||
private val moreKeys: Array<out KeyPair>
|
||||
) : ServiceHub {
|
||||
|
||||
companion object {
|
||||
|
||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
||||
|
||||
val cordappPaths = TestCordappDirectories.forPackages(packages)
|
||||
return JarScanningCordappLoader.fromDirectories(cordappPaths)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make properties appropriate for creating a DataSource for unit tests.
|
||||
*
|
||||
@ -97,7 +108,8 @@ open class MockServices private constructor(
|
||||
initialIdentity: TestIdentity,
|
||||
networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
vararg moreKeys: KeyPair): Pair<CordaPersistence, MockServices> {
|
||||
val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages)
|
||||
|
||||
val cordappLoader = cordappLoaderForPackages(cordappPackages)
|
||||
val dataSourceProps = makeTestDataSourceProperties()
|
||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||
val database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService)
|
||||
@ -119,14 +131,6 @@ open class MockServices private constructor(
|
||||
return Pair(database, mockService)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun getCallerPackage(): String {
|
||||
// TODO: In Java 9 there's a new stack walker API that is better than this.
|
||||
// The magic number '3' here is to chop off this method, an invisible bridge method generated by the
|
||||
// compiler and then the c'tor itself.
|
||||
return Throwable().stackTrace[3].className.split('.').dropLast(1).joinToString(".")
|
||||
}
|
||||
|
||||
// Because Kotlin is dumb and makes not publicly visible objects public, thus changing the public API.
|
||||
private val mockStateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage()
|
||||
}
|
||||
@ -150,39 +154,38 @@ open class MockServices private constructor(
|
||||
* (you can get one from [makeTestIdentityService]) and represents the given identity.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>,
|
||||
constructor(cordappPackages: Iterable<String>,
|
||||
initialIdentity: TestIdentity,
|
||||
identityService: IdentityService = makeTestIdentityService(),
|
||||
vararg moreKeys: KeyPair) :
|
||||
this(CordappLoader.createWithTestPackages(cordappPackages), identityService, testNetworkParameters(), initialIdentity, moreKeys)
|
||||
this(cordappLoaderForPackages(cordappPackages), identityService, testNetworkParameters(), initialIdentity, moreKeys)
|
||||
|
||||
constructor(cordappPackages: List<String>,
|
||||
constructor(cordappPackages: Iterable<String>,
|
||||
initialIdentity: TestIdentity,
|
||||
identityService: IdentityService,
|
||||
networkParameters: NetworkParameters,
|
||||
vararg moreKeys: KeyPair) :
|
||||
this(CordappLoader.createWithTestPackages(cordappPackages), identityService, networkParameters, initialIdentity, moreKeys)
|
||||
this(cordappLoaderForPackages(cordappPackages), identityService, networkParameters, initialIdentity, moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that looks for app code in the given package names, uses the provided identity service
|
||||
* (you can get one from [makeTestIdentityService]) and represents the given identity.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair) :
|
||||
constructor(cordappPackages: Iterable<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair) :
|
||||
this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that can't load CorDapp code, which uses the provided identity service
|
||||
* (you can get one from [makeTestIdentityService]) and which represents the given identity.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) :
|
||||
constructor(cordappPackages: Iterable<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) :
|
||||
this(cordappPackages, TestIdentity(initialIdentityName), identityService)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that can't load CorDapp code, and which uses a default service identity.
|
||||
*/
|
||||
constructor(cordappPackages: List<String>) : this(cordappPackages, CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
constructor(cordappPackages: Iterable<String>) : this(cordappPackages, CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses the provided identity service
|
||||
@ -190,7 +193,7 @@ open class MockServices private constructor(
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair)
|
||||
: this(listOf(getCallerPackage()), TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
: this(listOf(getCallerPackage(MockServices::class)!!), TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses the provided identity service
|
||||
@ -198,7 +201,7 @@ open class MockServices private constructor(
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService())
|
||||
: this(listOf(getCallerPackage()), TestIdentity(initialIdentityName), identityService)
|
||||
: this(listOf(getCallerPackage(MockServices::class)!!), TestIdentity(initialIdentityName), identityService)
|
||||
|
||||
/**
|
||||
* A helper constructor that requires at least one test identity to be registered, and which takes the package of
|
||||
@ -207,7 +210,7 @@ open class MockServices private constructor(
|
||||
* it is aware of.
|
||||
*/
|
||||
constructor(firstIdentity: TestIdentity, vararg moreIdentities: TestIdentity) : this(
|
||||
listOf(getCallerPackage()),
|
||||
listOf(getCallerPackage(MockServices::class)!!),
|
||||
firstIdentity,
|
||||
makeTestIdentityService(*listOf(firstIdentity, *moreIdentities).map { it.identity }.toTypedArray()),
|
||||
firstIdentity.keyPair
|
||||
@ -217,7 +220,7 @@ open class MockServices private constructor(
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses a default service
|
||||
* identity.
|
||||
*/
|
||||
constructor() : this(listOf(getCallerPackage()), CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
constructor() : this(listOf(getCallerPackage(MockServices::class)!!), CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
|
||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||
txs.forEach {
|
||||
@ -236,7 +239,9 @@ open class MockServices private constructor(
|
||||
return NodeInfo(listOf(NetworkHostAndPort("mock.node.services", 10000)), listOf(initialIdentity.identity), 1, serial = 1L)
|
||||
}
|
||||
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
|
||||
|
||||
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments, networkParameters.whitelistedContractImplementations)
|
||||
|
||||
override val cordappProvider: CordappProvider get() = mockCordappProvider
|
||||
|
||||
protected val servicesForResolution: ServicesForResolution get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParameters, validatedTransactions)
|
||||
|
@ -54,6 +54,7 @@ import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import rx.Subscription
|
||||
@ -87,20 +88,19 @@ class DriverDSLImpl(
|
||||
val isDebug: Boolean,
|
||||
val startNodesInProcess: Boolean,
|
||||
val waitForAllNodesToFinish: Boolean,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
val jmxPolicy: JmxPolicy,
|
||||
val notarySpecs: List<NotarySpec>,
|
||||
val compatibilityZone: CompatibilityZoneParams?,
|
||||
val networkParameters: NetworkParameters,
|
||||
val notaryCustomOverrides: Map<String, Any?>,
|
||||
val inMemoryDB: Boolean
|
||||
val inMemoryDB: Boolean,
|
||||
val cordappsForAllNodes: Set<TestCorDapp>
|
||||
) : InternalDriverDSL {
|
||||
|
||||
private var _executorService: ScheduledExecutorService? = null
|
||||
val executorService get() = _executorService!!
|
||||
private var _shutdownManager: ShutdownManager? = null
|
||||
override val shutdownManager get() = _shutdownManager!!
|
||||
private val cordappPackages = extraCordappPackagesToScan + getCallerPackage()
|
||||
// Map from a nodes legal name to an observable emitting the number of nodes in its network map.
|
||||
private val networkVisibilityController = NetworkVisibilityController()
|
||||
/**
|
||||
@ -186,6 +186,8 @@ class DriverDSLImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun startNode(defaultParameters: NodeParameters, providedName: CordaX500Name?, rpcUsers: List<User>, verifierType: VerifierType, customOverrides: Map<String, Any?>, startInSameProcess: Boolean?, maximumHeapSize: String) = startNode(defaultParameters, providedName, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, defaultParameters.additionalCordapps, defaultParameters.regenerateCordappsOnStart)
|
||||
|
||||
override fun startNode(
|
||||
defaultParameters: NodeParameters,
|
||||
providedName: CordaX500Name?,
|
||||
@ -193,7 +195,9 @@ class DriverDSLImpl(
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Set<TestCorDapp>,
|
||||
regenerateCordappsOnStart: Boolean
|
||||
): CordaFuture<NodeHandle> {
|
||||
val p2pAddress = portAllocation.nextHostAndPort()
|
||||
// TODO: Derive name from the full picked name, don't just wrap the common name
|
||||
@ -209,7 +213,7 @@ class DriverDSLImpl(
|
||||
return registrationFuture.flatMap {
|
||||
networkMapAvailability.flatMap {
|
||||
// But starting the node proper does require the network map
|
||||
startRegisteredNode(name, it, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, p2pAddress)
|
||||
startRegisteredNode(name, it, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, p2pAddress, additionalCordapps, regenerateCordappsOnStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,7 +225,9 @@ class DriverDSLImpl(
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean? = null,
|
||||
maximumHeapSize: String = "512m",
|
||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort()): CordaFuture<NodeHandle> {
|
||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
|
||||
additionalCordapps: Set<TestCorDapp> = emptySet(),
|
||||
regenerateCordappsOnStart: Boolean = false): CordaFuture<NodeHandle> {
|
||||
val rpcAddress = portAllocation.nextHostAndPort()
|
||||
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
||||
val webAddress = portAllocation.nextHostAndPort()
|
||||
@ -249,7 +255,7 @@ class DriverDSLImpl(
|
||||
allowMissingConfig = true,
|
||||
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
|
||||
)).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap)
|
||||
return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap, additionalCordapps, regenerateCordappsOnStart)
|
||||
}
|
||||
|
||||
private fun startNodeRegistration(providedName: CordaX500Name, rootCert: X509Certificate, compatibilityZoneURL: URL): CordaFuture<NodeConfig> {
|
||||
@ -303,6 +309,7 @@ class DriverDSLImpl(
|
||||
NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH"))
|
||||
}
|
||||
|
||||
// TODO remove this
|
||||
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
||||
check(notarySpecs.isEmpty()) { "Specify notaries in the CordformDefinition" }
|
||||
check(compatibilityZone == null) { "Cordform nodes cannot be run with compatibilityZoneURL" }
|
||||
@ -360,6 +367,7 @@ class DriverDSLImpl(
|
||||
}.transpose()
|
||||
}
|
||||
|
||||
// TODO remove this
|
||||
private fun startCordformNode(cordform: CordformNode, localNetworkMap: LocalNetworkMap): CordaFuture<NodeHandle> {
|
||||
val name = CordaX500Name.parse(cordform.name)
|
||||
// TODO We shouldn't have to allocate an RPC or web address if they're not specified. We're having to do this because of startNodeInternal
|
||||
@ -388,9 +396,8 @@ class DriverDSLImpl(
|
||||
allowMissingConfig = true,
|
||||
configOverrides = rawConfig.toNodeOnly()
|
||||
)
|
||||
val cordaConfig = typesafe.parseAsNodeConfiguration()
|
||||
val config = NodeConfig(rawConfig, cordaConfig).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap)
|
||||
val config = NodeConfig(typesafe).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap, emptySet())
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@ -614,7 +621,6 @@ class DriverDSLImpl(
|
||||
jolokiaJarPath,
|
||||
monitorPort,
|
||||
systemProperties,
|
||||
cordappPackages,
|
||||
"512m",
|
||||
*extraCmdLineFlag
|
||||
)
|
||||
@ -624,13 +630,19 @@ class DriverDSLImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun startNodeInternal(config: NodeConfig,
|
||||
private val sharedCordappsDirectories: Iterable<Path> by lazy {
|
||||
TestCordappDirectories.cached(cordappsForAllNodes)
|
||||
}
|
||||
|
||||
private fun startNodeInternal(specifiedConfig: NodeConfig,
|
||||
webAddress: NetworkHostAndPort,
|
||||
startInProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
localNetworkMap: LocalNetworkMap?): CordaFuture<NodeHandle> {
|
||||
val visibilityHandle = networkVisibilityController.register(config.corda.myLegalName)
|
||||
val baseDirectory = config.corda.baseDirectory.createDirectories()
|
||||
localNetworkMap: LocalNetworkMap?,
|
||||
additionalCordapps: Set<TestCorDapp>,
|
||||
regenerateCordappsOnStart: Boolean = false): CordaFuture<NodeHandle> {
|
||||
val visibilityHandle = networkVisibilityController.register(specifiedConfig.corda.myLegalName)
|
||||
val baseDirectory = specifiedConfig.corda.baseDirectory.createDirectories()
|
||||
localNetworkMap?.networkParametersCopier?.install(baseDirectory)
|
||||
localNetworkMap?.nodeInfosCopier?.addConfig(baseDirectory)
|
||||
|
||||
@ -639,10 +651,16 @@ class DriverDSLImpl(
|
||||
visibilityHandle.close()
|
||||
}
|
||||
|
||||
val useHTTPS = config.typesafe.run { hasPath("useHTTPS") && getBoolean("useHTTPS") }
|
||||
val useHTTPS = specifiedConfig.typesafe.run { hasPath("useHTTPS") && getBoolean("useHTTPS") }
|
||||
|
||||
val existingCorDappDirectoriesOption = if (regenerateCordappsOnStart) emptyList<String>() else if (specifiedConfig.typesafe.hasPath(NodeConfiguration.cordappDirectoriesKey)) specifiedConfig.typesafe.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
||||
|
||||
val cordappDirectories = existingCorDappDirectoriesOption + sharedCordappsDirectories.map { it.toString() } + TestCordappDirectories.cached(additionalCordapps, regenerateCordappsOnStart).map { it.toString() }
|
||||
|
||||
val config = NodeConfig(specifiedConfig.typesafe.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories)))
|
||||
|
||||
if (startInProcess ?: startNodesInProcess) {
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config, cordappPackages)
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config)
|
||||
shutdownManager.registerShutdown(
|
||||
nodeAndThreadFuture.map { (node, thread) ->
|
||||
{
|
||||
@ -669,7 +687,7 @@ class DriverDSLImpl(
|
||||
} else {
|
||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||
val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null
|
||||
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, systemProperties, cordappPackages, maximumHeapSize)
|
||||
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, systemProperties, maximumHeapSize)
|
||||
|
||||
// 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
|
||||
@ -763,10 +781,12 @@ class DriverDSLImpl(
|
||||
|
||||
private fun <A> oneOf(array: Array<A>) = array[Random().nextInt(array.size)]
|
||||
|
||||
fun cordappsInCurrentAndAdditionalPackages(packagesToScan: Iterable<String> = emptySet()): Set<TestCorDapp> = cordappsForPackages(getCallerPackage() + packagesToScan)
|
||||
fun cordappsInCurrentAndAdditionalPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(otherPackages.toList() + firstPackage)
|
||||
|
||||
private fun startInProcessNode(
|
||||
executorService: ScheduledExecutorService,
|
||||
config: NodeConfig,
|
||||
cordappPackages: List<String>
|
||||
config: NodeConfig
|
||||
): CordaFuture<Pair<StartedNode<Node>, Thread>> {
|
||||
return executorService.fork {
|
||||
log.info("Starting in-process Node ${config.corda.myLegalName.organisation}")
|
||||
@ -776,7 +796,7 @@ class DriverDSLImpl(
|
||||
// Write node.conf
|
||||
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
|
||||
// TODO pass the version in?
|
||||
val node = InProcessNode(config.corda, MOCK_VERSION_INFO, cordappPackages).start()
|
||||
val node = InProcessNode(config.corda, MOCK_VERSION_INFO).start()
|
||||
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
||||
node.internals.run()
|
||||
}
|
||||
@ -793,10 +813,10 @@ class DriverDSLImpl(
|
||||
jolokiaJarPath: String,
|
||||
monitorPort: Int?,
|
||||
overriddenSystemProperties: Map<String, String>,
|
||||
cordappPackages: List<String>,
|
||||
maximumHeapSize: String,
|
||||
vararg extraCmdLineFlag: String
|
||||
): Process {
|
||||
|
||||
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
|
||||
"debug port is " + (debugPort ?: "not enabled") + ", " +
|
||||
"jolokia monitoring port is " + (monitorPort ?: "not enabled"))
|
||||
@ -810,11 +830,6 @@ class DriverDSLImpl(
|
||||
)
|
||||
|
||||
systemProperties += inheritFromParentProcess()
|
||||
|
||||
if (cordappPackages.isNotEmpty()) {
|
||||
systemProperties += Node.scanPackagesSystemProperty to cordappPackages.joinToString(Node.scanPackagesSeparator)
|
||||
}
|
||||
|
||||
systemProperties += overriddenSystemProperties
|
||||
|
||||
// See experimental/quasar-hook/README.md for how to generate.
|
||||
@ -1064,13 +1079,13 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
isDebug = defaultParameters.isDebug,
|
||||
startNodesInProcess = defaultParameters.startNodesInProcess,
|
||||
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
|
||||
extraCordappPackagesToScan = defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy = defaultParameters.jmxPolicy,
|
||||
notarySpecs = defaultParameters.notarySpecs,
|
||||
compatibilityZone = null,
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = defaultParameters.cordappsForAllNodes()
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
@ -1144,12 +1159,12 @@ fun <A> internalDriver(
|
||||
startNodesInProcess: Boolean = DriverParameters().startNodesInProcess,
|
||||
waitForAllNodesToFinish: Boolean = DriverParameters().waitForAllNodesToFinish,
|
||||
notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs,
|
||||
extraCordappPackagesToScan: List<String> = DriverParameters().extraCordappPackagesToScan,
|
||||
jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy,
|
||||
networkParameters: NetworkParameters = DriverParameters().networkParameters,
|
||||
compatibilityZone: CompatibilityZoneParams? = null,
|
||||
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
|
||||
inMemoryDB: Boolean = DriverParameters().inMemoryDB,
|
||||
cordappsForAllNodes: Set<TestCorDapp> = DriverParameters().cordappsForAllNodes(),
|
||||
dsl: DriverDSLImpl.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -1163,12 +1178,12 @@ fun <A> internalDriver(
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = compatibilityZone,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -1188,3 +1203,5 @@ fun writeConfig(path: Path, filename: String, config: Config) {
|
||||
private fun Config.toNodeOnly(): Config {
|
||||
return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this
|
||||
}
|
||||
|
||||
internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
|
@ -12,10 +12,7 @@ import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.createDirectory
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.messaging.MessageRecipients
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
@ -25,14 +22,12 @@ import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.hours
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.NodePropertiesStore
|
||||
import net.corda.node.services.api.SchemaService
|
||||
import net.corda.node.services.config.*
|
||||
@ -49,6 +44,7 @@ import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.setGlobalSerialization
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
@ -59,6 +55,7 @@ import org.apache.sshd.common.util.security.SecurityUtils
|
||||
import rx.internal.schedulers.CachedThreadScheduler
|
||||
import java.math.BigInteger
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.time.Clock
|
||||
@ -76,8 +73,7 @@ data class MockNodeArgs(
|
||||
val network: InternalMockNetwork,
|
||||
val id: Int,
|
||||
val entropyRoot: BigInteger,
|
||||
val version: VersionInfo = MOCK_VERSION_INFO,
|
||||
val extraCordappPackages: List<String> = emptyList()
|
||||
val version: VersionInfo = MOCK_VERSION_INFO
|
||||
)
|
||||
|
||||
data class InternalMockNodeParameters(
|
||||
@ -86,25 +82,26 @@ data class InternalMockNodeParameters(
|
||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
val version: VersionInfo = MOCK_VERSION_INFO,
|
||||
val extraCordappPackages: List<String> = emptyList()) {
|
||||
val additionalCordapps: Set<TestCorDapp>? = null) {
|
||||
constructor(mockNodeParameters: MockNodeParameters) : this(
|
||||
mockNodeParameters.forcedID,
|
||||
mockNodeParameters.legalName,
|
||||
mockNodeParameters.entropyRoot,
|
||||
mockNodeParameters.configOverrides,
|
||||
MOCK_VERSION_INFO,
|
||||
mockNodeParameters.extraCordappPackages
|
||||
mockNodeParameters.additionalCordapps
|
||||
)
|
||||
}
|
||||
|
||||
open class InternalMockNetwork(private val cordappPackages: List<String> = emptyList(),
|
||||
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
||||
val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||
networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
val defaultFactory: (MockNodeArgs) -> MockNode = InternalMockNetwork::MockNode) {
|
||||
val defaultFactory: (MockNodeArgs, CordappLoader?) -> MockNode = { args, cordappLoader -> cordappLoader?.let { MockNode(args, it) } ?: MockNode(args) },
|
||||
val cordappsForAllNodes: Set<TestCorDapp> = emptySet()) {
|
||||
init {
|
||||
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
||||
// This SFTP support loads BouncyCastle, which we want to avoid.
|
||||
@ -113,6 +110,10 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
require(networkParameters.notaries.isEmpty()) { "Define notaries using notarySpecs" }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val logger = loggerFor<InternalMockNetwork>()
|
||||
}
|
||||
|
||||
var nextNodeId = 0
|
||||
private set
|
||||
private val filesystem = Jimfs.newFileSystem(unix())
|
||||
@ -129,6 +130,10 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
}
|
||||
private val sharedUserCount = AtomicInteger(0)
|
||||
|
||||
private val sharedCorDappsDirectories: Iterable<Path> by lazy {
|
||||
TestCordappDirectories.cached(cordappsForAllNodes)
|
||||
}
|
||||
|
||||
/** A read only view of the current set of nodes. */
|
||||
val nodes: List<MockNode> get() = _nodes
|
||||
|
||||
@ -221,12 +226,11 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
}
|
||||
}
|
||||
|
||||
open class MockNode(args: MockNodeArgs) : AbstractNode(
|
||||
open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories)) : AbstractNode(
|
||||
args.config,
|
||||
TestClock(Clock.systemUTC()),
|
||||
args.version,
|
||||
// Add the specified additional CorDapps.
|
||||
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages + args.extraCordappPackages),
|
||||
cordappLoader,
|
||||
args.network.busyLatch
|
||||
) {
|
||||
companion object {
|
||||
@ -356,7 +360,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
return createUnstartedNode(parameters, defaultFactory)
|
||||
}
|
||||
|
||||
fun <N : MockNode> createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): N {
|
||||
fun <N : MockNode> createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> N): N {
|
||||
return createNodeImpl(parameters, nodeFactory, false)
|
||||
}
|
||||
|
||||
@ -365,11 +369,11 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
}
|
||||
|
||||
/** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */
|
||||
fun <N : MockNode> createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
||||
fun <N : MockNode> createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> N): StartedNode<N> {
|
||||
return uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!!
|
||||
}
|
||||
|
||||
private fun <N : MockNode> createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N {
|
||||
private fun <N : MockNode> createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> N, start: Boolean): N {
|
||||
val id = parameters.forcedID ?: nextNodeId++
|
||||
val config = mockNodeConfiguration().also {
|
||||
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory
|
||||
@ -378,7 +382,12 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys
|
||||
parameters.configOverrides(it)
|
||||
}
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version, parameters.extraCordappPackages))
|
||||
|
||||
val cordapps: Set<TestCorDapp> = parameters.additionalCordapps ?: emptySet()
|
||||
val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps)
|
||||
doReturn(cordappDirectories).whenever(config).cordappDirectories
|
||||
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version), JarScanningCordappLoader.fromDirectories(cordappDirectories))
|
||||
_nodes += node
|
||||
if (start) {
|
||||
node.start()
|
||||
@ -386,7 +395,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
return node
|
||||
}
|
||||
|
||||
fun <N : MockNode> restartNode(node: StartedNode<N>, nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
||||
fun <N : MockNode> restartNode(node: StartedNode<N>, nodeFactory: (MockNodeArgs, CordappLoader?) -> N): StartedNode<N> {
|
||||
node.internals.disableDBCloseOnStop()
|
||||
node.dispose()
|
||||
return createNode(
|
||||
@ -397,7 +406,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
|
||||
fun restartNode(node: StartedNode<MockNode>): StartedNode<MockNode> = restartNode(node, defaultFactory)
|
||||
|
||||
fun baseDirectory(nodeId: Int): Path = filesystem.getPath("/nodes/$nodeId")
|
||||
fun baseDirectory(nodeId: Int): Path = testDirectory / "nodes/$nodeId"
|
||||
|
||||
/**
|
||||
* Asks every node in order to process any queued up inbound messages. This may in turn result in nodes
|
||||
@ -455,7 +464,6 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
fun waitQuiescent() {
|
||||
busyLatch.await()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
open class MessagingServiceSpy(val messagingService: MessagingService) : MessagingService by messagingService
|
||||
|
@ -0,0 +1,73 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
|
||||
internal class MutableTestCorDapp private constructor(override val name: String, override val version: String, override val vendor: String, override val title: String, private val willResourceBeAddedToCorDapp: (String, URL) -> Boolean, private val jarEntries: Set<JarEntryInfo>) : TestCorDapp.Mutable {
|
||||
|
||||
constructor(name: String, version: String, vendor: String, title: String, classes: Set<Class<*>>, willResourceBeAddedToCorDapp: (String, URL) -> Boolean) : this(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntriesFromClasses(classes))
|
||||
|
||||
companion object {
|
||||
|
||||
private const val jarExtension = ".jar"
|
||||
private const val whitespace = " "
|
||||
private const val whitespaceReplacement = "_"
|
||||
private val productionPathSegments = setOf<(String) -> String>({ "out${File.separator}production${File.separator}classes" }, { fullyQualifiedName -> "main${File.separator}${fullyQualifiedName.packageToPath()}" })
|
||||
private val excludedCordaPackages = setOf("net.corda.core", "net.corda.node")
|
||||
|
||||
fun filterTestCorDappClass(fullyQualifiedName: String, url: URL): Boolean {
|
||||
|
||||
return isTestResource(fullyQualifiedName, url) || !isInExcludedCordaPackage(fullyQualifiedName)
|
||||
}
|
||||
|
||||
private fun isTestResource(fullyQualifiedName: String, url: URL): Boolean {
|
||||
|
||||
return productionPathSegments.map { it.invoke(fullyQualifiedName) }.none { url.toString().contains(it) }
|
||||
}
|
||||
|
||||
private fun isInExcludedCordaPackage(packageName: String): Boolean {
|
||||
|
||||
return excludedCordaPackages.any { packageName.startsWith(it) }
|
||||
}
|
||||
|
||||
private fun jarEntriesFromClasses(classes: Set<Class<*>>): Set<JarEntryInfo> {
|
||||
|
||||
val illegal = classes.filter { it.protectionDomain?.codeSource?.location == null }
|
||||
if (illegal.isNotEmpty()) {
|
||||
throw IllegalArgumentException("Some classes do not have a location, typically because they are part of Java or Kotlin. Offending types were: ${illegal.joinToString(", ", "[", "]") { it.simpleName }}")
|
||||
}
|
||||
return classes.map(Class<*>::jarEntryInfo).toSet()
|
||||
}
|
||||
}
|
||||
|
||||
override val classes: Set<Class<*>> = jarEntries.filterIsInstance(JarEntryInfo.ClassJarEntryInfo::class.java).map(JarEntryInfo.ClassJarEntryInfo::clazz).toSet()
|
||||
|
||||
override val resources: Set<URL> = jarEntries.map(JarEntryInfo::url).toSet()
|
||||
|
||||
override fun withName(name: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withTitle(title: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withVersion(version: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withVendor(vendor: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withClasses(classes: Set<Class<*>>) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun plusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all + packageClasses })
|
||||
|
||||
override fun minusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all - packageClasses })
|
||||
|
||||
override fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries + JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
||||
|
||||
override fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries - JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
||||
|
||||
override fun packageAsJarWithPath(jarFilePath: Path) = jarEntries.packageToCorDapp(jarFilePath, name, version, vendor, title, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun packageAsJarInDirectory(parentDirectory: Path): Path = (parentDirectory / defaultJarName()).also { packageAsJarWithPath(it) }
|
||||
|
||||
private fun defaultJarName(): String = "${name}_$version$jarExtension".replace(whitespace, whitespaceReplacement)
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
@ -7,10 +8,10 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
@ -29,10 +30,12 @@ import java.nio.file.Path
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
// TODO Some of the logic here duplicates what's in the driver
|
||||
// TODO Some of the logic here duplicates what's in the driver - the reason why it's not straightforward to replace it by using DriverDSLImpl in `init()` and `stopAllNodes()` is because of the platform version passed to nodes (driver doesn't support this, and it's a property of the Corda JAR)
|
||||
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) {
|
||||
companion object {
|
||||
private val WHITESPACE = "\\s++".toRegex()
|
||||
|
||||
private val logger = loggerFor<NodeBasedTest>()
|
||||
}
|
||||
|
||||
@Rule
|
||||
@ -99,14 +102,23 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
) + configOverrides
|
||||
)
|
||||
|
||||
val parsedConfig = config.parseAsNodeConfiguration().also { nodeConfiguration ->
|
||||
val cordapps = cordappsForPackages(getCallerPackage(NodeBasedTest::class)?.let { cordappPackages + it } ?: cordappPackages)
|
||||
|
||||
val existingCorDappDirectoriesOption = if (config.hasPath(NodeConfiguration.cordappDirectoriesKey)) config.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
||||
|
||||
val cordappDirectories = existingCorDappDirectoriesOption + TestCordappDirectories.cached(cordapps).map { it.toString() }
|
||||
|
||||
val specificConfig = config.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories))
|
||||
|
||||
val parsedConfig = specificConfig.parseAsNodeConfiguration().also { nodeConfiguration ->
|
||||
val errors = nodeConfiguration.validate()
|
||||
if (errors.isNotEmpty()) {
|
||||
throw IllegalStateException("Invalid node configuration. Errors where:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}")
|
||||
}
|
||||
}
|
||||
|
||||
defaultNetworkParameters.install(baseDirectory)
|
||||
val node = InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion), cordappPackages).start()
|
||||
val node = InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion)).start()
|
||||
nodes += node
|
||||
ensureAllNetworkMapCachesHaveAllNodeInfos()
|
||||
thread(name = legalName.organisation) {
|
||||
@ -130,8 +142,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
}
|
||||
}
|
||||
|
||||
class InProcessNode(
|
||||
configuration: NodeConfiguration, versionInfo: VersionInfo, cordappPackages: List<String>) : Node(
|
||||
configuration, versionInfo, false, CordappLoader.createDefaultWithTestPackages(configuration, cordappPackages)) {
|
||||
class InProcessNode(configuration: NodeConfiguration, versionInfo: VersionInfo) : Node(configuration, versionInfo, false) {
|
||||
|
||||
override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
|
||||
}
|
||||
|
@ -27,8 +27,10 @@ import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.TransportConfiguration
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
|
||||
@ -109,13 +111,13 @@ fun <A> rpcDriver(
|
||||
useTestClock: Boolean = false,
|
||||
startNodesInProcess: Boolean = false,
|
||||
waitForNodesToFinish: Boolean = false,
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
externalTrace: Trace? = null,
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
inMemoryDB: Boolean = true,
|
||||
cordappsForAllNodes: Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(),
|
||||
dsl: RPCDriverDSL.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -129,13 +131,13 @@ fun <A> rpcDriver(
|
||||
isDebug = isDebug,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = null,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
), externalTrace
|
||||
),
|
||||
coerce = { it },
|
||||
|
@ -0,0 +1,70 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.deleteRecursively
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
|
||||
internal object TestCordappDirectories {
|
||||
|
||||
private val logger = loggerFor<TestCordappDirectories>()
|
||||
|
||||
private const val whitespace = " "
|
||||
private const val whitespaceReplacement = "_"
|
||||
|
||||
private val cordappsCache: ConcurrentMap<List<String>, Path> = ConcurrentHashMap<List<String>, Path>()
|
||||
|
||||
internal fun cached(cordapps: Iterable<TestCorDapp>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
||||
|
||||
cordappsDirectory.toFile().deleteOnExit()
|
||||
return cordapps.map { cached(it, replaceExistingOnes, cordappsDirectory) }
|
||||
}
|
||||
|
||||
internal fun cached(cordapp: TestCorDapp, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Path {
|
||||
|
||||
cordappsDirectory.toFile().deleteOnExit()
|
||||
val cacheKey = cordapp.resources.map { it.toExternalForm() }.sorted()
|
||||
return if (replaceExistingOnes) {
|
||||
cordappsCache.remove(cacheKey)
|
||||
cordappsCache.getOrPut(cacheKey) {
|
||||
|
||||
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
||||
cordappDirectory.createDirectories()
|
||||
cordapp.packageAsJarInDirectory(cordappDirectory)
|
||||
cordappDirectory
|
||||
}
|
||||
} else {
|
||||
cordappsCache.getOrPut(cacheKey) {
|
||||
|
||||
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
||||
cordappDirectory.createDirectories()
|
||||
cordapp.packageAsJarInDirectory(cordappDirectory)
|
||||
cordappDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun forPackages(packages: Iterable<String>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
||||
|
||||
cordappsDirectory.toFile().deleteOnExit()
|
||||
val cordapps = simplifyScanPackages(packages).distinct().fold(emptySet<TestCorDapp>()) { all, packageName -> all + testCorDapp(packageName) }
|
||||
return cached(cordapps, replaceExistingOnes, cordappsDirectory)
|
||||
}
|
||||
|
||||
private val defaultCordappsDirectory: Path by lazy {
|
||||
|
||||
val cordappsDirectory = (Paths.get("build") / "tmp" / getTimestampAsDirectoryName() / "generated-test-cordapps").toAbsolutePath()
|
||||
logger.info("Initialising generated test CorDapps directory in $cordappsDirectory")
|
||||
if (cordappsDirectory.exists()) {
|
||||
cordappsDirectory.deleteRecursively()
|
||||
}
|
||||
cordappsDirectory.createDirectories()
|
||||
cordappsDirectory
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.deleteIfExists
|
||||
import net.corda.core.internal.outputStream
|
||||
import net.corda.node.internal.cordapp.createTestManifest
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Packages some [JarEntryInfo] into a CorDapp JAR with specified [path].
|
||||
* @param path The path of the JAR.
|
||||
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
||||
*/
|
||||
internal fun Iterable<JarEntryInfo>.packageToCorDapp(path: Path, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }) {
|
||||
|
||||
var hasContent = false
|
||||
try {
|
||||
hasContent = packageToCorDapp(path.outputStream(), name, version, vendor, title, willResourceBeAddedBeToCorDapp)
|
||||
} finally {
|
||||
if (!hasContent) {
|
||||
path.deleteIfExists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages some [JarEntryInfo] into a CorDapp JAR using specified [outputStream].
|
||||
* @param outputStream The [OutputStream] for the JAR.
|
||||
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
||||
*/
|
||||
internal fun Iterable<JarEntryInfo>.packageToCorDapp(outputStream: OutputStream, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }): Boolean {
|
||||
|
||||
val manifest = createTestManifest(name, title, version, vendor)
|
||||
return JarOutputStream(outputStream, manifest).use { jos -> zip(jos, willResourceBeAddedBeToCorDapp) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a [Class] into a [JarEntryInfo].
|
||||
*/
|
||||
internal fun Class<*>.jarEntryInfo(): JarEntryInfo {
|
||||
|
||||
return JarEntryInfo.ClassJarEntryInfo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages some [TestCorDapp]s under a root [directory], each with it's own JAR.
|
||||
* @param directory The parent directory in which CorDapp JAR will be created.
|
||||
*/
|
||||
fun Iterable<TestCorDapp>.packageInDirectory(directory: Path) {
|
||||
|
||||
directory.createDirectories()
|
||||
forEach { cordapp -> cordapp.packageAsJarInDirectory(directory) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all classes within the [targetPackage].
|
||||
*/
|
||||
fun allClassesForPackage(targetPackage: String): Set<Class<*>> {
|
||||
|
||||
val scanResult = FastClasspathScanner(targetPackage).strictWhitelist().scan()
|
||||
return scanResult.namesOfAllClasses.filter { className -> className.startsWith(targetPackage) }.map(scanResult::classNameToClassRef).toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps each package to a [TestCorDapp] with resources found in that package.
|
||||
*/
|
||||
fun cordappsForPackages(packages: Iterable<String>): Set<TestCorDapp> {
|
||||
|
||||
return simplifyScanPackages(packages).toSet().fold(emptySet()) { all, packageName -> all + testCorDapp(packageName) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps each package to a [TestCorDapp] with resources found in that package.
|
||||
*/
|
||||
fun cordappsForPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> {
|
||||
|
||||
return cordappsForPackages(setOf(*otherPackages) + firstPackage)
|
||||
}
|
||||
|
||||
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
|
||||
|
||||
val stackTrace = Throwable().stackTrace
|
||||
val index = stackTrace.indexOfLast { it.className == directCallerClass.java.name }
|
||||
if (index == -1) return null
|
||||
return try {
|
||||
Class.forName(stackTrace[index + 1].className)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getCallerPackage(directCallerClass: KClass<*>): String? {
|
||||
|
||||
return getCallerClass(directCallerClass)?.`package`?.name
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [TestCorDapp] containing resources found in [packageName].
|
||||
*/
|
||||
internal fun testCorDapp(packageName: String): TestCorDapp {
|
||||
|
||||
val uuid = UUID.randomUUID()
|
||||
val name = "$packageName-$uuid"
|
||||
val version = "$uuid"
|
||||
return TestCorDapp.Factory.create(name, version).plusPackage(packageName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Squashes child packages if the parent is present. Example: ["com.foo", "com.foo.bar"] into just ["com.foo"].
|
||||
*/
|
||||
fun simplifyScanPackages(scanPackages: Iterable<String>): List<String> {
|
||||
|
||||
return scanPackages.sorted().fold(emptyList()) { listSoFar, packageName ->
|
||||
when {
|
||||
listSoFar.isEmpty() -> listOf(packageName)
|
||||
packageName.startsWith(listSoFar.last()) -> listSoFar
|
||||
else -> listSoFar + packageName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a class or package name into a path segment.
|
||||
*/
|
||||
internal fun String.packageToPath() = replace(".", File.separator)
|
||||
|
||||
private fun Iterable<JarEntryInfo>.zip(outputStream: ZipOutputStream, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean): Boolean {
|
||||
|
||||
val entries = filter { (fullyQualifiedName, url) -> willResourceBeAddedBeToCorDapp(fullyQualifiedName, url) }
|
||||
if (entries.isNotEmpty()) {
|
||||
zip(outputStream, entries)
|
||||
}
|
||||
return entries.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun zip(outputStream: ZipOutputStream, allInfo: Iterable<JarEntryInfo>) {
|
||||
|
||||
val time = FileTime.from(Instant.EPOCH)
|
||||
val classLoader = Thread.currentThread().contextClassLoader
|
||||
allInfo.distinctBy { it.url }.sortedBy { it.url.toExternalForm() }.forEach { info ->
|
||||
|
||||
try {
|
||||
val entry = ZipEntry(info.entryName).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
||||
outputStream.putNextEntry(entry)
|
||||
classLoader.getResourceAsStream(info.entryName).use {
|
||||
IOUtils.copy(it, outputStream)
|
||||
}
|
||||
} finally {
|
||||
outputStream.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single resource to be added to a CorDapp JAR.
|
||||
*/
|
||||
internal sealed class JarEntryInfo(val fullyQualifiedName: String, val url: URL) {
|
||||
|
||||
abstract val entryName: String
|
||||
|
||||
/**
|
||||
* Represents a class to be added to a CorDapp JAR.
|
||||
*/
|
||||
class ClassJarEntryInfo(val clazz: Class<*>) : JarEntryInfo(clazz.name, clazz.classFileURL()) {
|
||||
|
||||
override val entryName = "${fullyQualifiedName.packageToPath()}$fileExtensionSeparator$classFileExtension"
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a resource file to be added to a CorDapp JAR.
|
||||
*/
|
||||
class ResourceJarEntryInfo(fullyQualifiedName: String, url: URL) : JarEntryInfo(fullyQualifiedName, url) {
|
||||
|
||||
override val entryName: String
|
||||
get() {
|
||||
val extensionIndex = fullyQualifiedName.lastIndexOf(fileExtensionSeparator)
|
||||
return "${fullyQualifiedName.substring(0 until extensionIndex).packageToPath()}${fullyQualifiedName.substring(extensionIndex)}"
|
||||
}
|
||||
}
|
||||
|
||||
operator fun component1(): String = fullyQualifiedName
|
||||
|
||||
operator fun component2(): URL = url
|
||||
|
||||
private companion object {
|
||||
|
||||
private const val classFileExtension = "class"
|
||||
private const val fileExtensionSeparator = "."
|
||||
private const val whitespace = " "
|
||||
private const val whitespaceReplacement = "%20"
|
||||
|
||||
private fun Class<*>.classFileURL(): URL {
|
||||
|
||||
require(protectionDomain?.codeSource?.location != null) { "Invalid class $name for test CorDapp. Classes without protection domain cannot be referenced. This typically happens for Java / Kotlin types." }
|
||||
return URI.create("${protectionDomain.codeSource.location}/${name.packageToPath()}$fileExtensionSeparator$classFileExtension".escaped()).toURL()
|
||||
}
|
||||
|
||||
private fun String.escaped(): String = this.replace(whitespace, whitespaceReplacement)
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
|
||||
/**
|
||||
@ -59,12 +60,12 @@ class CordformNodeRunner(private val cordformDefinition: CordformDefinition) {
|
||||
internalDriver(
|
||||
jmxPolicy = JmxPolicy(true),
|
||||
driverDirectory = cordformDefinition.nodesDirectory,
|
||||
extraCordappPackagesToScan = extraPackagesToScan,
|
||||
// Notaries are manually specified in Cordform so we don't want the driver automatically starting any
|
||||
notarySpecs = emptyList(),
|
||||
// Start from after the largest port used to prevent port clash
|
||||
portAllocation = PortAllocation.Incremental(maxPort + 1),
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages(extraPackagesToScan)
|
||||
) {
|
||||
cordformDefinition.setup(this)
|
||||
startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running
|
||||
|
@ -9,7 +9,7 @@ class InternalMockNetworkTests {
|
||||
fun `does not leak serialization env if init fails`() {
|
||||
val e = Exception("didn't work")
|
||||
assertThatThrownBy {
|
||||
object : InternalMockNetwork() {
|
||||
object : InternalMockNetwork(cordappsForAllNodes = emptySet()) {
|
||||
override fun createNotaries() = throw e
|
||||
}
|
||||
}.isSameAs(e)
|
||||
|
Reference in New Issue
Block a user