mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Syncing Cordapp info code from ENT so that ENT-1731 is fully ported (#3914)
Also, Cordapp.Info has been made internal as it's not used in the public API
This commit is contained in:
@ -48,20 +48,4 @@ interface Cordapp {
|
|||||||
val jarPath: URL
|
val jarPath: URL
|
||||||
val cordappClasses: List<String>
|
val cordappClasses: List<String>
|
||||||
val jarHash: SecureHash.SHA256
|
val jarHash: SecureHash.SHA256
|
||||||
|
}
|
||||||
/**
|
|
||||||
* CorDapp's information, including vendor and version.
|
|
||||||
*
|
|
||||||
* @property shortName Cordapp's shortName
|
|
||||||
* @property vendor Cordapp's vendor
|
|
||||||
* @property version Cordapp's version
|
|
||||||
*/
|
|
||||||
@DoNotImplement
|
|
||||||
interface Info {
|
|
||||||
val shortName: String
|
|
||||||
val vendor: String
|
|
||||||
val version: String
|
|
||||||
|
|
||||||
fun hasUnknownFields(): Boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -24,25 +24,28 @@ data class CordappImpl(
|
|||||||
override val customSchemas: Set<MappedSchema>,
|
override val customSchemas: Set<MappedSchema>,
|
||||||
override val allFlows: List<Class<out FlowLogic<*>>>,
|
override val allFlows: List<Class<out FlowLogic<*>>>,
|
||||||
override val jarPath: URL,
|
override val jarPath: URL,
|
||||||
|
val info: Info,
|
||||||
override val jarHash: SecureHash.SHA256) : Cordapp {
|
override val jarHash: SecureHash.SHA256) : Cordapp {
|
||||||
override val name: String = jarPath.toPath().fileName.toString().removeSuffix(".jar")
|
override val name: String = jarName(jarPath)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun jarName(url: URL): String = url.toPath().fileName.toString().removeSuffix(".jar")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An exhaustive list of all classes relevant to the node within this CorDapp
|
* An exhaustive list of all classes relevant to the node within this CorDapp
|
||||||
*
|
*
|
||||||
* TODO: Also add [SchedulableFlow] as a Cordapp class
|
* TODO: Also add [SchedulableFlow] as a Cordapp class
|
||||||
*/
|
*/
|
||||||
override val cordappClasses = ((rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames)
|
override val cordappClasses: List<String> = (rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames
|
||||||
|
|
||||||
data class Info(override val shortName: String, override val vendor: String, override val version: String): Cordapp.Info {
|
// TODO Why a seperate Info class and not just have the fields directly in CordappImpl?
|
||||||
|
data class Info(val shortName: String, val vendor: String, val version: String) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val UNKNOWN_VALUE = "Unknown"
|
private const val UNKNOWN_VALUE = "Unknown"
|
||||||
|
|
||||||
val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE)
|
val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasUnknownFields(): Boolean {
|
fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
|
||||||
return setOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,11 +71,22 @@ jar {
|
|||||||
exclude "META-INF/*.MF"
|
exclude "META-INF/*.MF"
|
||||||
exclude "META-INF/LICENSE"
|
exclude "META-INF/LICENSE"
|
||||||
exclude "META-INF/NOTICE"
|
exclude "META-INF/NOTICE"
|
||||||
|
|
||||||
|
manifest {
|
||||||
|
attributes(
|
||||||
|
"Manifest-Version": "1.0",
|
||||||
|
"Specification-Title": description,
|
||||||
|
"Specification-Version": version,
|
||||||
|
"Specification-Vendor": "Corda Open Source",
|
||||||
|
"Implementation-Title": "$group.$baseName",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cordapp {
|
cordapp {
|
||||||
info {
|
info {
|
||||||
vendor "R3"
|
name "net/corda/finance"
|
||||||
|
vendor "Corda Open Source"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,4 +14,9 @@ data class VersionInfo(
|
|||||||
/** The exact version control commit ID of the node build. */
|
/** The exact version control commit ID of the node build. */
|
||||||
val revision: String,
|
val revision: String,
|
||||||
/** The node vendor */
|
/** The node vendor */
|
||||||
val vendor: String)
|
val vendor: String) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val UNKNOWN = VersionInfo(1, "Unknown", "Unknown", "Unknown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.node.cordapp
|
|||||||
|
|
||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,7 +13,7 @@ interface CordappLoader {
|
|||||||
/**
|
/**
|
||||||
* Returns all [Cordapp]s found.
|
* Returns all [Cordapp]s found.
|
||||||
*/
|
*/
|
||||||
val cordapps: List<Cordapp>
|
val cordapps: List<CordappImpl>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a [ClassLoader] containing all types from all [Cordapp]s.
|
* Returns a [ClassLoader] containing all types from all [Cordapp]s.
|
||||||
|
@ -88,7 +88,7 @@ class NodeWithInfo(val node: Node, val info: NodeInfo) {
|
|||||||
open class Node(configuration: NodeConfiguration,
|
open class Node(configuration: NodeConfiguration,
|
||||||
versionInfo: VersionInfo,
|
versionInfo: VersionInfo,
|
||||||
private val initialiseSerialization: Boolean = true,
|
private val initialiseSerialization: Boolean = true,
|
||||||
cordappLoader: CordappLoader = makeCordappLoader(configuration)
|
cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo)
|
||||||
) : AbstractNode<NodeInfo>(
|
) : AbstractNode<NodeInfo>(
|
||||||
configuration,
|
configuration,
|
||||||
createClock(configuration),
|
createClock(configuration),
|
||||||
@ -130,9 +130,11 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val sameVmNodeCounter = AtomicInteger()
|
private val sameVmNodeCounter = AtomicInteger()
|
||||||
private fun makeCordappLoader(configuration: NodeConfiguration): CordappLoader {
|
|
||||||
return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories)
|
private fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader {
|
||||||
|
return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this configurable.
|
// TODO: make this configurable.
|
||||||
const val MAX_RPC_MESSAGE_SIZE = 10485760
|
const val MAX_RPC_MESSAGE_SIZE = 10485760
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import net.corda.cliutils.ExitCodes
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.concurrent.thenMatch
|
import net.corda.core.internal.concurrent.thenMatch
|
||||||
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
import net.corda.core.internal.errors.AddressBindingException
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
import net.corda.core.utilities.Try
|
import net.corda.core.utilities.Try
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
@ -325,7 +326,8 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val nodeInfo = node.start()
|
val nodeInfo = node.start()
|
||||||
Node.printBasicNodeInfo("Loaded CorDapps", node.services.cordappProvider.cordapps.joinToString { it.name })
|
logLoadedCorDapps(node.services.cordappProvider.cordapps)
|
||||||
|
|
||||||
node.nodeReadyFuture.thenMatch({
|
node.nodeReadyFuture.thenMatch({
|
||||||
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
|
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
|
||||||
val name = nodeInfo.legalIdentitiesAndCerts.first().name.organisation
|
val name = nodeInfo.legalIdentitiesAndCerts.first().name.organisation
|
||||||
@ -407,6 +409,17 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open protected fun logLoadedCorDapps(corDapps: List<CordappImpl>) {
|
||||||
|
fun CordappImpl.Info.description() = "$shortName version $version by $vendor"
|
||||||
|
|
||||||
|
Node.printBasicNodeInfo("Loaded ${corDapps.size} CorDapp(s)", corDapps.map { it.info }.joinToString(", ", transform = CordappImpl.Info::description))
|
||||||
|
corDapps.map { it.info }.filter { it.hasUnknownFields() }.let { malformed ->
|
||||||
|
if (malformed.isNotEmpty()) {
|
||||||
|
logger.warn("Found ${malformed.size} CorDapp(s) with unknown information. They will be unable to run on Corda in the future.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun enforceSingleNodeIsRunning(baseDirectory: Path) {
|
private fun enforceSingleNodeIsRunning(baseDirectory: Path) {
|
||||||
// Write out our process ID (which may or may not resemble a UNIX process id - to us it's just a string) to a
|
// Write out our process ID (which may or may not resemble a UNIX process id - to us it's just a string) to a
|
||||||
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
|
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.cordapp.CordappContext
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
|
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
|
||||||
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
import net.corda.core.internal.createCordappContext
|
import net.corda.core.internal.createCordappContext
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.node.services.AttachmentId
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
@ -34,7 +35,7 @@ open class CordappProviderImpl(private val cordappLoader: CordappLoader,
|
|||||||
/**
|
/**
|
||||||
* Current known CorDapps loaded on this node
|
* Current known CorDapps loaded on this node
|
||||||
*/
|
*/
|
||||||
override val cordapps get() = cordappLoader.cordapps
|
override val cordapps: List<CordappImpl> get() = cordappLoader.cordapps
|
||||||
|
|
||||||
fun start(whitelistedContractImplementations: Map<String, List<AttachmentId>>) {
|
fun start(whitelistedContractImplementations: Map<String, List<AttachmentId>>) {
|
||||||
cordappAttachments.putAll(loadContractsIntoAttachmentStore())
|
cordappAttachments.putAll(loadContractsIntoAttachmentStore())
|
||||||
|
@ -3,8 +3,9 @@ package net.corda.node.internal.cordapp
|
|||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
|
|
||||||
interface CordappProviderInternal : CordappProvider {
|
interface CordappProviderInternal : CordappProvider {
|
||||||
val cordapps: List<Cordapp>
|
val cordapps: List<CordappImpl>
|
||||||
fun getCordappForFlow(flowLogic: FlowLogic<*>): Cordapp?
|
fun getCordappForFlow(flowLogic: FlowLogic<*>): Cordapp?
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import net.corda.core.serialization.SerializationCustomSerializer
|
|||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.cordapp.CordappLoader
|
import net.corda.node.cordapp.CordappLoader
|
||||||
import net.corda.node.internal.classloading.requireAnnotation
|
import net.corda.node.internal.classloading.requireAnnotation
|
||||||
import net.corda.nodeapi.internal.coreContractClasses
|
import net.corda.nodeapi.internal.coreContractClasses
|
||||||
@ -24,6 +25,7 @@ import java.net.URL
|
|||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.jar.JarInputStream
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
|
|
||||||
@ -32,9 +34,10 @@ import kotlin.streams.toList
|
|||||||
*
|
*
|
||||||
* @property cordappJarPaths The classpath of cordapp JARs
|
* @property cordappJarPaths The classpath of cordapp JARs
|
||||||
*/
|
*/
|
||||||
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>) : CordappLoaderTemplate() {
|
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>,
|
||||||
|
versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() {
|
||||||
|
|
||||||
override val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
|
override val cordapps: List<CordappImpl> by lazy { loadCordapps() + coreCordapp }
|
||||||
|
|
||||||
override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
|
override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
|
||||||
|
|
||||||
@ -54,10 +57,9 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
*
|
*
|
||||||
* @param corDappDirectories Directories used to scan for CorDapp JARs.
|
* @param corDappDirectories Directories used to scan for CorDapp JARs.
|
||||||
*/
|
*/
|
||||||
fun fromDirectories(corDappDirectories: Iterable<Path>): CordappLoader {
|
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
|
||||||
|
|
||||||
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
|
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
|
||||||
return JarScanningCordappLoader(corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() })
|
return JarScanningCordappLoader(corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }, versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,7 +67,9 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
*
|
*
|
||||||
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
|
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
|
||||||
*/
|
*/
|
||||||
fun fromJarUrls(scanJars: List<URL>) = JarScanningCordappLoader(scanJars.map { it.restricted() })
|
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
|
||||||
|
return JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo)
|
||||||
|
}
|
||||||
|
|
||||||
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
|
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
|
||||||
|
|
||||||
@ -86,31 +90,30 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
ContractUpgradeFlow.Initiate::class.java,
|
ContractUpgradeFlow.Initiate::class.java,
|
||||||
ContractUpgradeFlow.Authorise::class.java,
|
ContractUpgradeFlow.Authorise::class.java,
|
||||||
ContractUpgradeFlow.Deauthorise::class.java)
|
ContractUpgradeFlow.Deauthorise::class.java)
|
||||||
|
|
||||||
/** A Cordapp representing the core package which is not scanned automatically. */
|
|
||||||
@VisibleForTesting
|
|
||||||
internal val coreCordapp = CordappImpl(
|
|
||||||
contractClassNames = listOf(),
|
|
||||||
initiatedFlows = listOf(),
|
|
||||||
rpcFlows = coreRPCFlows,
|
|
||||||
serviceFlows = listOf(),
|
|
||||||
schedulableFlows = listOf(),
|
|
||||||
services = listOf(),
|
|
||||||
serializationWhitelists = listOf(),
|
|
||||||
serializationCustomSerializers = listOf(),
|
|
||||||
customSchemas = setOf(),
|
|
||||||
allFlows = listOf(),
|
|
||||||
jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location
|
|
||||||
jarHash = SecureHash.allOnesHash
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCordapps(): List<Cordapp> {
|
/** A Cordapp representing the core package which is not scanned automatically. */
|
||||||
return cordappJarPaths.map { scanCordapp(it).toCordapp(it) }
|
@VisibleForTesting
|
||||||
}
|
internal val coreCordapp = CordappImpl(
|
||||||
|
contractClassNames = listOf(),
|
||||||
|
initiatedFlows = listOf(),
|
||||||
|
rpcFlows = coreRPCFlows,
|
||||||
|
serviceFlows = listOf(),
|
||||||
|
schedulableFlows = listOf(),
|
||||||
|
services = listOf(),
|
||||||
|
serializationWhitelists = listOf(),
|
||||||
|
serializationCustomSerializers = listOf(),
|
||||||
|
customSchemas = setOf(),
|
||||||
|
info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion),
|
||||||
|
allFlows = listOf(),
|
||||||
|
jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location
|
||||||
|
jarHash = SecureHash.allOnesHash
|
||||||
|
)
|
||||||
|
|
||||||
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): Cordapp {
|
private fun loadCordapps(): List<CordappImpl> = cordappJarPaths.map { scanCordapp(it).toCordapp(it) }
|
||||||
|
|
||||||
|
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl {
|
||||||
|
val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(CordappImpl.jarName(url.url))
|
||||||
return CordappImpl(
|
return CordappImpl(
|
||||||
findContractClassNames(this),
|
findContractClassNames(this),
|
||||||
findInitiatedFlows(this),
|
findInitiatedFlows(this),
|
||||||
@ -123,6 +126,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
findCustomSchemas(this),
|
findCustomSchemas(this),
|
||||||
findAllFlows(this),
|
findAllFlows(this),
|
||||||
url.url,
|
url.url,
|
||||||
|
info,
|
||||||
getJarHash(url.url)
|
getJarHash(url.url)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -268,24 +272,23 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
class MultipleCordappsForFlowException(message: String) : Exception(message)
|
class MultipleCordappsForFlowException(message: String) : Exception(message)
|
||||||
|
|
||||||
abstract class CordappLoaderTemplate : CordappLoader {
|
abstract class CordappLoaderTemplate : CordappLoader {
|
||||||
|
|
||||||
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
||||||
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
||||||
.groupBy { it.first }
|
.groupBy { it.first }
|
||||||
.mapValues {
|
.mapValues { entry ->
|
||||||
if(it.value.size > 1) { throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow ${it.value.first().first.name}: [ ${it.value.joinToString { it.second.name }} ].") }
|
if (entry.value.size > 1) {
|
||||||
it.value.single().second
|
throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow " +
|
||||||
|
"${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.name }} ].")
|
||||||
|
}
|
||||||
|
entry.value.single().second
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val cordappSchemas: Set<MappedSchema> by lazy {
|
override val cordappSchemas: Set<MappedSchema> by lazy {
|
||||||
|
|
||||||
cordapps.flatMap { it.customSchemas }.toSet()
|
cordapps.flatMap { it.customSchemas }.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val appClassLoader: ClassLoader by lazy {
|
override val appClassLoader: ClassLoader by lazy {
|
||||||
|
|
||||||
URLClassLoader(cordapps.stream().map { it.jarPath }.toTypedArray(), javaClass.classLoader)
|
URLClassLoader(cordapps.stream().map { it.jarPath }.toTypedArray(), javaClass.classLoader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package net.corda.node.internal.cordapp
|
package net.corda.node.internal.cordapp
|
||||||
|
|
||||||
import net.corda.core.cordapp.Cordapp
|
|
||||||
import net.corda.core.internal.cordapp.CordappImpl
|
import net.corda.core.internal.cordapp.CordappImpl
|
||||||
import java.util.*
|
|
||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import java.util.jar.Manifest
|
import java.util.jar.Manifest
|
||||||
|
|
||||||
fun createTestManifest(name: String, title: String, version: String, vendor: String): Manifest {
|
fun createTestManifest(name: String, title: String, version: String, vendor: String): Manifest {
|
||||||
|
|
||||||
val manifest = Manifest()
|
val manifest = Manifest()
|
||||||
|
|
||||||
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
|
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
|
||||||
@ -27,21 +24,19 @@ fun createTestManifest(name: String, title: String, version: String, vendor: Str
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun Manifest.set(key: String, value: String) {
|
operator fun Manifest.set(key: String, value: String) {
|
||||||
|
|
||||||
mainAttributes.putValue(key, value)
|
mainAttributes.putValue(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Manifest?.toCordappInfo(defaultShortName: String): Cordapp.Info {
|
fun Manifest?.toCordappInfo(defaultShortName: String): CordappImpl.Info {
|
||||||
|
var info = CordappImpl.Info.UNKNOWN
|
||||||
var unknown = CordappImpl.Info.UNKNOWN
|
|
||||||
(this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName ->
|
(this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName ->
|
||||||
unknown = unknown.copy(shortName = shortName)
|
info = info.copy(shortName = shortName)
|
||||||
}
|
}
|
||||||
this?.mainAttributes?.getValue("Implementation-Vendor")?.let { vendor ->
|
this?.mainAttributes?.getValue("Implementation-Vendor")?.let { vendor ->
|
||||||
unknown = unknown.copy(vendor = vendor)
|
info = info.copy(vendor = vendor)
|
||||||
}
|
}
|
||||||
this?.mainAttributes?.getValue("Implementation-Version")?.let { version ->
|
this?.mainAttributes?.getValue("Implementation-Version")?.let { version ->
|
||||||
unknown = unknown.copy(version = version)
|
info = info.copy(version = version)
|
||||||
}
|
}
|
||||||
return unknown
|
return info
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,9 @@ class NodeTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `generateAndSaveNodeInfo works`() {
|
fun `generateAndSaveNodeInfo works`() {
|
||||||
val configuration = createConfig(ALICE_NAME)
|
val configuration = createConfig(ALICE_NAME)
|
||||||
val platformVersion = 789
|
val info = VersionInfo(789, "3.0", "SNAPSHOT", "R3")
|
||||||
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use {
|
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use {
|
||||||
val versionInfo = rigorousMock<VersionInfo>().also {
|
val node = Node(configuration, info, initialiseSerialization = false)
|
||||||
doReturn(platformVersion).whenever(it).platformVersion
|
|
||||||
}
|
|
||||||
val node = Node(configuration, versionInfo, initialiseSerialization = false)
|
|
||||||
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
|
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,9 +84,8 @@ class NodeTest {
|
|||||||
// Save some NodeInfo
|
// Save some NodeInfo
|
||||||
session.save(persistentNodeInfo)
|
session.save(persistentNodeInfo)
|
||||||
}
|
}
|
||||||
val node = Node(configuration, rigorousMock<VersionInfo>().also {
|
val versionInfo = VersionInfo(10, "3.0", "SNAPSHOT", "R3")
|
||||||
doReturn(10).whenever(it).platformVersion
|
val node = Node(configuration, versionInfo, initialiseSerialization = false)
|
||||||
}, initialiseSerialization = false)
|
|
||||||
assertThat(getAllInfos(it)).isNotEmpty
|
assertThat(getAllInfos(it)).isNotEmpty
|
||||||
node.clearNetworkMapCache()
|
node.clearNetworkMapCache()
|
||||||
assertThat(getAllInfos(it)).isEmpty()
|
assertThat(getAllInfos(it)).isEmpty()
|
||||||
@ -97,7 +93,7 @@ class NodeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Node can start with multiple keypairs for it's identity`() {
|
fun `Node can start with multiple keypairs for its identity`() {
|
||||||
val configuration = createConfig(ALICE_NAME)
|
val configuration = createConfig(ALICE_NAME)
|
||||||
val (nodeInfo1, _) = createNodeInfoAndSigned(ALICE_NAME)
|
val (nodeInfo1, _) = createNodeInfoAndSigned(ALICE_NAME)
|
||||||
val (nodeInfo2, _) = createNodeInfoAndSigned(ALICE_NAME)
|
val (nodeInfo2, _) = createNodeInfoAndSigned(ALICE_NAME)
|
||||||
@ -135,6 +131,8 @@ class NodeTest {
|
|||||||
|
|
||||||
val node = Node(configuration, rigorousMock<VersionInfo>().also {
|
val node = Node(configuration, rigorousMock<VersionInfo>().also {
|
||||||
doReturn(10).whenever(it).platformVersion
|
doReturn(10).whenever(it).platformVersion
|
||||||
|
doReturn("test-vendor").whenever(it).vendor
|
||||||
|
doReturn("1.0").whenever(it).releaseVersion
|
||||||
}, initialiseSerialization = false)
|
}, initialiseSerialization = false)
|
||||||
|
|
||||||
//this throws an exception with old behaviour
|
//this throws an exception with old behaviour
|
||||||
|
@ -46,7 +46,7 @@ class JarScanningCordappLoaderTest {
|
|||||||
fun `test that classes that aren't in cordapps aren't loaded`() {
|
fun `test that classes that aren't in cordapps aren't loaded`() {
|
||||||
// Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp
|
// Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp
|
||||||
val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get(".")))
|
val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get(".")))
|
||||||
assertThat(loader.cordapps).containsOnly(JarScanningCordappLoader.coreCordapp)
|
assertThat(loader.cordapps).containsOnly(loader.coreCordapp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -57,7 +57,7 @@ class JarScanningCordappLoaderTest {
|
|||||||
val actual = loader.cordapps.toTypedArray()
|
val actual = loader.cordapps.toTypedArray()
|
||||||
assertThat(actual).hasSize(2)
|
assertThat(actual).hasSize(2)
|
||||||
|
|
||||||
val actualCordapp = actual.single { it != JarScanningCordappLoader.coreCordapp }
|
val actualCordapp = actual.single { it != loader.coreCordapp }
|
||||||
assertThat(actualCordapp.contractClassNames).isEqualTo(listOf(isolatedContractId))
|
assertThat(actualCordapp.contractClassNames).isEqualTo(listOf(isolatedContractId))
|
||||||
assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor")
|
assertThat(actualCordapp.initiatedFlows.single().name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Acceptor")
|
||||||
assertThat(actualCordapp.rpcFlows).isEmpty()
|
assertThat(actualCordapp.rpcFlows).isEmpty()
|
||||||
@ -91,7 +91,7 @@ class JarScanningCordappLoaderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `sub-packages are ignored`() {
|
fun `sub-packages are ignored`() {
|
||||||
val loader = cordappLoaderForPackages(listOf("net.corda", testScanPackage))
|
val loader = cordappLoaderForPackages(listOf("net.corda.core", testScanPackage))
|
||||||
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
|
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
|
||||||
assertThat(cordapps).hasSize(1)
|
assertThat(cordapps).hasSize(1)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ class AttachmentDemoTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `attachment demo using a 10MB zip file`() {
|
fun `attachment demo using a 10MB zip file`() {
|
||||||
val numOfExpectedBytes = 10_000_000
|
val numOfExpectedBytes = 10_000_000
|
||||||
driver(DriverParameters(portAllocation = PortAllocation.Incremental(20000))) {
|
driver(DriverParameters(portAllocation = PortAllocation.Incremental(20000), startNodesInProcess = true)) {
|
||||||
val demoUser = listOf(User("demo", "demo", setOf(all())))
|
val demoUser = listOf(User("demo", "demo", setOf(all())))
|
||||||
val (nodeA, nodeB) = listOf(
|
val (nodeA, nodeB) = listOf(
|
||||||
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
||||||
|
@ -33,6 +33,7 @@ class MockCordappProvider(
|
|||||||
serializationCustomSerializers = emptyList(),
|
serializationCustomSerializers = emptyList(),
|
||||||
customSchemas = emptySet(),
|
customSchemas = emptySet(),
|
||||||
jarPath = Paths.get("").toUri().toURL(),
|
jarPath = Paths.get("").toUri().toURL(),
|
||||||
|
info = CordappImpl.Info.UNKNOWN,
|
||||||
allFlows = emptyList(),
|
allFlows = emptyList(),
|
||||||
jarHash = SecureHash.allOnesHash)
|
jarHash = SecureHash.allOnesHash)
|
||||||
if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) {
|
if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) {
|
||||||
@ -40,7 +41,9 @@ class MockCordappProvider(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName)
|
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? {
|
||||||
|
return cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName)
|
||||||
|
}
|
||||||
|
|
||||||
private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId {
|
private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId {
|
||||||
val existingAttachment = attachments.files.filter {
|
val existingAttachment = attachments.files.filter {
|
||||||
|
Reference in New Issue
Block a user