CORDA-676 Eager cordapp schemas (#1839)

* Retire customSchemas.
* Key cordapp-to-hash map by url as native equality too strict.
This commit is contained in:
Andrzej Cichocki
2017-10-16 11:35:29 +01:00
committed by GitHub
parent d3d87c2497
commit 38cf4a489e
46 changed files with 197 additions and 236 deletions

View File

@ -37,7 +37,7 @@ import kotlin.test.assertFailsWith
class AttachmentLoadingTests : TestDependencyInjectionBase() {
private class Services : MockServices() {
private val provider = CordappProviderImpl(CordappLoader.createDevMode(listOf(isolatedJAR))).start(attachments)
private val provider = CordappProviderImpl(CordappLoader.createDevMode(listOf(isolatedJAR)), attachments)
private val cordapp get() = provider.cordapps.first()
val attachmentId get() = provider.getCordappAttachmentId(cordapp)!!
val appContext get() = provider.getAppContext(cordapp)

View File

@ -7,7 +7,6 @@ import net.corda.confidential.SwapIdentitiesFlow
import net.corda.confidential.SwapIdentitiesHandler
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.cordapp.CordappProvider
import net.corda.core.flows.*
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
@ -26,7 +25,6 @@ import net.corda.core.node.ServiceHub
import net.corda.core.node.StateLoader
import net.corda.core.node.services.*
import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
@ -37,6 +35,7 @@ import net.corda.node.VersionInfo
import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.CordappProviderInternal
import net.corda.node.services.ContractUpgradeHandler
import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler
@ -148,7 +147,6 @@ abstract class AbstractNode(config: NodeConfiguration,
protected lateinit var network: MessagingService
protected val runOnStop = ArrayList<() -> Any?>()
protected lateinit var database: CordaPersistence
lateinit var cordappProvider: CordappProviderImpl
protected val _nodeReadyFuture = openFuture<Unit>()
/** Completes once the node has successfully registered with the network map service
* or has loaded network map data from local database */
@ -161,7 +159,7 @@ abstract class AbstractNode(config: NodeConfiguration,
}
open val serializationWhitelists: List<SerializationWhitelist> by lazy {
cordappProvider.cordapps.flatMap { it.serializationWhitelists }
cordappLoader.cordapps.flatMap { it.serializationWhitelists }
}
/** Set to non-null once [start] has been successfully called. */
@ -185,11 +183,12 @@ abstract class AbstractNode(config: NodeConfiguration,
validateKeystore()
}
private fun makeSchemaService() = NodeSchemaService(cordappLoader)
open fun generateNodeInfo() {
check(started == null) { "Node has already been started" }
initCertificate()
log.info("Generating nodeInfo ...")
val schemaService = NodeSchemaService()
val schemaService = makeSchemaService()
initialiseDatabasePersistence(schemaService) {
makeServices(schemaService)
saveOwnNodeInfo()
@ -200,7 +199,7 @@ abstract class AbstractNode(config: NodeConfiguration,
check(started == null) { "Node has already been started" }
initCertificate()
log.info("Node starting up ...")
val schemaService = NodeSchemaService()
val schemaService = makeSchemaService()
// Do all of this in a database transaction so anything that might need a connection has one.
val startedImpl = initialiseDatabasePersistence(schemaService) {
val tokenizableServices = makeServices(schemaService)
@ -231,8 +230,7 @@ abstract class AbstractNode(config: NodeConfiguration,
installCordaServices()
registerCordappFlows()
_services.rpcFlows += cordappProvider.cordapps.flatMap { it.rpcFlows }
registerCustomSchemas(cordappProvider.cordapps.flatMap { it.customSchemas }.toSet())
_services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
FlowLogicRefFactoryImpl.classloader = cordappLoader.appClassLoader
runOnStop += network::stop
@ -254,7 +252,7 @@ abstract class AbstractNode(config: NodeConfiguration,
private class ServiceInstantiationException(cause: Throwable?) : CordaException("Service Instantiation Error", cause)
private fun installCordaServices() {
val loadedServices = cordappProvider.cordapps.flatMap { it.services }
val loadedServices = cordappLoader.cordapps.flatMap { it.services }
filterServicesToInstall(loadedServices).forEach {
try {
installCordaService(it)
@ -374,7 +372,7 @@ abstract class AbstractNode(config: NodeConfiguration,
}
private fun registerCordappFlows() {
cordappProvider.cordapps.flatMap { it.initiatedFlows }
cordappLoader.cordapps.flatMap { it.initiatedFlows }
.forEach {
try {
registerInitiatedFlowInternal(it, track = false)
@ -471,11 +469,11 @@ abstract class AbstractNode(config: NodeConfiguration,
*/
private fun makeServices(schemaService: SchemaService): MutableList<Any> {
checkpointStorage = DBCheckpointStorage()
cordappProvider = CordappProviderImpl(cordappLoader)
val transactionStorage = makeTransactionStorage()
_services = ServiceHubInternalImpl(schemaService, transactionStorage, StateLoaderImpl(transactionStorage))
attachments = NodeAttachmentService(services.monitoringService.metrics)
cordappProvider.start(attachments)
val metrics = MetricRegistry()
attachments = NodeAttachmentService(metrics)
val cordappProvider = CordappProviderImpl(cordappLoader, attachments)
_services = ServiceHubInternalImpl(schemaService, transactionStorage, StateLoaderImpl(transactionStorage), MonitoringService(metrics), cordappProvider)
legalIdentity = obtainIdentity(notaryConfig = null)
network = makeMessagingService(legalIdentity)
info = makeInfo(legalIdentity)
@ -541,7 +539,7 @@ abstract class AbstractNode(config: NodeConfiguration,
protected open fun <T> initialiseDatabasePersistence(schemaService: SchemaService, insideTransaction: () -> T): T {
val props = configuration.dataSourceProperties
if (props.isNotEmpty()) {
this.database = configureDatabase(props, configuration.database, schemaService, { _services.identityService })
this.database = configureDatabase(props, configuration.database, { _services.identityService }, schemaService)
// Now log the vendor string as this will also cause a connection to be tested eagerly.
database.transaction {
log.info("Connected to ${database.dataSource.connection.metaData.databaseProductName} database.")
@ -764,12 +762,13 @@ abstract class AbstractNode(config: NodeConfiguration,
private inner class ServiceHubInternalImpl(
override val schemaService: SchemaService,
override val validatedTransactions: WritableTransactionStorage,
private val stateLoader: StateLoader
private val stateLoader: StateLoader,
override val monitoringService: MonitoringService,
override val cordappProvider: CordappProviderInternal
) : SingletonSerializeAsToken(), ServiceHubInternal, StateLoader by stateLoader {
override val rpcFlows = ArrayList<Class<out FlowLogic<*>>>()
override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage()
override val auditService = DummyAuditService()
override val monitoringService = MonitoringService(MetricRegistry())
override val transactionVerifierService by lazy { makeTransactionVerifierService() }
override val networkMapCache by lazy { PersistentNetworkMapCache(this) }
override val vaultService by lazy { NodeVaultService(platformClock, keyManagementService, stateLoader, this@AbstractNode.database.hibernateConfig) }
@ -794,8 +793,6 @@ abstract class AbstractNode(config: NodeConfiguration,
override val myInfo: NodeInfo get() = info
override val database: CordaPersistence get() = this@AbstractNode.database
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
override val cordappProvider: CordappProvider = this@AbstractNode.cordappProvider
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist")
@ -817,9 +814,4 @@ abstract class AbstractNode(config: NodeConfiguration,
override fun jdbcSession(): Connection = database.createSession()
}
fun registerCustomSchemas(schemas: Set<MappedSchema>) {
database.hibernateConfig.schemaService.registerCustomSchemas(schemas)
}
}

View File

@ -62,7 +62,7 @@ import kotlin.system.exitProcess
*
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
*/
open class Node(override val configuration: FullNodeConfiguration,
open class Node(configuration: FullNodeConfiguration,
versionInfo: VersionInfo,
val initialiseSerialization: Boolean = true,
cordappLoader: CordappLoader = makeCordappLoader(configuration)
@ -99,6 +99,7 @@ open class Node(override val configuration: FullNodeConfiguration,
}
override val log: Logger get() = logger
override val configuration get() = super.configuration as FullNodeConfiguration // Necessary to avoid init order NPE.
override val networkMapAddress: NetworkMapAddress? get() = configuration.networkMapService?.address?.let(::NetworkMapAddress)
override fun makeTransactionVerifierService() = (network as NodeMessagingClient).verifierService

View File

@ -95,7 +95,7 @@ open class NodeStartup(val args: Array<String>) {
return
}
val startedNode = node.start()
Node.printBasicNodeInfo("Loaded CorDapps", startedNode.internals.cordappProvider.cordapps.joinToString { it.name })
Node.printBasicNodeInfo("Loaded CorDapps", startedNode.services.cordappProvider.cordapps.joinToString { it.name })
startedNode.internals.nodeReadyFuture.thenMatch({
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
val name = startedNode.info.legalIdentitiesAndCerts.first().name.organisation

View File

@ -38,10 +38,10 @@ import kotlin.streams.toList
*
* @property cordappJarPaths The classpath of cordapp JARs
*/
class CordappLoader private constructor(private val cordappJarPaths: List<URL>) {
class CordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>) {
val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
internal val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.toTypedArray(), javaClass.classLoader)
internal val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
init {
if (cordappJarPaths.isEmpty()) {
@ -97,20 +97,22 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection
*/
@VisibleForTesting
fun createDevMode(scanJars: List<URL>) = CordappLoader(scanJars)
fun createDevMode(scanJars: List<URL>) = CordappLoader(scanJars.map { RestrictedURL(it, null) })
private fun getCordappsPath(baseDir: Path): Path = baseDir / CORDAPPS_DIR_NAME
private fun createScanPackage(scanPackage: String): List<URL> {
private fun createScanPackage(scanPackage: String): List<RestrictedURL> {
val resource = scanPackage.replace('.', '/')
return this::class.java.classLoader.getResources(resource)
.asSequence()
.map { path ->
if (path.protocol == "jar") {
(path.openConnection() as JarURLConnection).jarFileURL.toURI()
// When running tests from gradle this may be a corda module jar, so restrict to scanPackage:
RestrictedURL((path.openConnection() as JarURLConnection).jarFileURL, scanPackage)
} else {
createDevCordappJar(scanPackage, path, resource)
}.toURL()
// No need to restrict as createDevCordappJar has already done that:
RestrictedURL(createDevCordappJar(scanPackage, path, resource).toURL(), null)
}
}
.toList()
}
@ -143,12 +145,12 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
return generatedCordapps[path]!!
}
private fun getCordappsInDirectory(cordappsDir: Path): List<URL> {
private fun getCordappsInDirectory(cordappsDir: Path): List<RestrictedURL> {
return if (!cordappsDir.exists()) {
emptyList<URL>()
emptyList()
} else {
cordappsDir.list {
it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.map { it.toUri().toURL() }.toList()
it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.map { RestrictedURL(it.toUri().toURL(), null) }.toList()
}
}
}
@ -187,15 +189,15 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
findServices(scanResult),
findPlugins(it),
findCustomSchemas(scanResult),
it)
it.url)
}
}
private fun findServices(scanResult: ScanResult): List<Class<out SerializeAsToken>> {
private fun findServices(scanResult: RestrictedScanResult): List<Class<out SerializeAsToken>> {
return scanResult.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class)
}
private fun findInitiatedFlows(scanResult: ScanResult): List<Class<out FlowLogic<*>>> {
private fun findInitiatedFlows(scanResult: RestrictedScanResult): List<Class<out FlowLogic<*>>> {
return scanResult.getClassesWithAnnotation(FlowLogic::class, InitiatedBy::class)
// First group by the initiating flow class in case there are multiple mappings
.groupBy { it.requireAnnotation<InitiatedBy>().value.java }
@ -214,35 +216,35 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
return Modifier.isPublic(modifiers) && !isLocalClass && !isAnonymousClass && (!isMemberClass || Modifier.isStatic(modifiers))
}
private fun findRPCFlows(scanResult: ScanResult): List<Class<out FlowLogic<*>>> {
private fun findRPCFlows(scanResult: RestrictedScanResult): List<Class<out FlowLogic<*>>> {
return scanResult.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class).filter { it.isUserInvokable() }
}
private fun findServiceFlows(scanResult: ScanResult): List<Class<out FlowLogic<*>>> {
private fun findServiceFlows(scanResult: RestrictedScanResult): List<Class<out FlowLogic<*>>> {
return scanResult.getClassesWithAnnotation(FlowLogic::class, StartableByService::class)
}
private fun findSchedulableFlows(scanResult: ScanResult): List<Class<out FlowLogic<*>>> {
private fun findSchedulableFlows(scanResult: RestrictedScanResult): List<Class<out FlowLogic<*>>> {
return scanResult.getClassesWithAnnotation(FlowLogic::class, SchedulableFlow::class)
}
private fun findContractClassNames(scanResult: ScanResult): List<String> {
return (scanResult.getNamesOfClassesImplementing(Contract::class.java) + scanResult.getNamesOfClassesImplementing(UpgradedContract::class.java)).distinct()
private fun findContractClassNames(scanResult: RestrictedScanResult): List<String> {
return (scanResult.getNamesOfClassesImplementing(Contract::class) + scanResult.getNamesOfClassesImplementing(UpgradedContract::class)).distinct()
}
private fun findPlugins(cordappJarPath: URL): List<SerializationWhitelist> {
return ServiceLoader.load(SerializationWhitelist::class.java, URLClassLoader(arrayOf(cordappJarPath), appClassLoader)).toList().filter {
cordappJarPath == it.javaClass.protectionDomain.codeSource.location
private fun findPlugins(cordappJarPath: RestrictedURL): List<SerializationWhitelist> {
return ServiceLoader.load(SerializationWhitelist::class.java, URLClassLoader(arrayOf(cordappJarPath.url), appClassLoader)).toList().filter {
it.javaClass.protectionDomain.codeSource.location == cordappJarPath.url && it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix)
} + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app.
}
private fun findCustomSchemas(scanResult: ScanResult): Set<MappedSchema> {
private fun findCustomSchemas(scanResult: RestrictedScanResult): Set<MappedSchema> {
return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet()
}
private fun scanCordapp(cordappJarPath: URL): ScanResult {
private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult {
logger.info("Scanning CorDapp in $cordappJarPath")
return FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath).scan()
return RestrictedScanResult(FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).scan(), cordappJarPath.qualifiedNamePrefix)
}
private class FlowTypeHierarchyComparator(val initiatingFlow: Class<out FlowLogic<*>>) : Comparator<Class<out FlowLogic<*>>> {
@ -269,16 +271,30 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
}
}
private fun <T : Any> ScanResult.getClassesWithSuperclass(type: KClass<T>): List<T> {
return getNamesOfSubclassesOf(type.java)
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
.map { it.kotlin.objectOrNewInstance() }
/** @param rootPackageName only this package and subpackages may be extracted from [url], or null to allow all packages. */
private class RestrictedURL(val url: URL, rootPackageName: String?) {
val qualifiedNamePrefix = rootPackageName?.let { it + '.' } ?: ""
}
private fun <T : Any> ScanResult.getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> {
return getNamesOfClassesWithAnnotation(annotation.java)
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
private inner class RestrictedScanResult(private val scanResult: ScanResult, private val qualifiedNamePrefix: String) {
fun getNamesOfClassesImplementing(type: KClass<*>): List<String> {
return scanResult.getNamesOfClassesImplementing(type.java)
.filter { it.startsWith(qualifiedNamePrefix) }
}
fun <T : Any> getClassesWithSuperclass(type: KClass<T>): List<T> {
return scanResult.getNamesOfSubclassesOf(type.java)
.filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
.map { it.kotlin.objectOrNewInstance() }
}
fun <T : Any> getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> {
return scanResult.getNamesOfClassesWithAnnotation(annotation.java)
.filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
}
}
}

View File

@ -6,15 +6,14 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappContext
import net.corda.core.cordapp.CordappProvider
import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.SingletonSerializeAsToken
import java.net.URLClassLoader
import java.net.URL
/**
* Cordapp provider and store. For querying CorDapps for their attachment and vice versa.
*/
open class CordappProviderImpl(private val cordappLoader: CordappLoader) : SingletonSerializeAsToken(), CordappProvider {
open class CordappProviderImpl(private val cordappLoader: CordappLoader, attachmentStorage: AttachmentStorage) : SingletonSerializeAsToken(), CordappProviderInternal {
override fun getAppContext(): CordappContext {
// TODO: Use better supported APIs in Java 9
Exception().stackTrace.forEach { stackFrame ->
@ -34,28 +33,19 @@ open class CordappProviderImpl(private val cordappLoader: CordappLoader) : Singl
/**
* Current known CorDapps loaded on this node
*/
val cordapps get() = cordappLoader.cordapps
private lateinit var cordappAttachments: HashBiMap<SecureHash, Cordapp>
/**
* Should only be called once from the initialisation routine of the node or tests
*/
fun start(attachmentStorage: AttachmentStorage): CordappProviderImpl {
cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore(attachmentStorage))
return this
}
override val cordapps get() = cordappLoader.cordapps
private val cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore(attachmentStorage))
/**
* Gets the attachment ID of this CorDapp. Only CorDapps with contracts have an attachment ID
*
* @param cordapp The cordapp to get the attachment ID
* @return An attachment ID if it exists, otherwise nothing
*/
fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp)
fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp.jarPath)
private fun loadContractsIntoAttachmentStore(attachmentStorage: AttachmentStorage): Map<SecureHash, Cordapp> {
val cordappsWithAttachments = cordapps.filter { !it.contractClassNames.isEmpty() }
val attachmentIds = cordappsWithAttachments.map { it.jarPath.openStream().use { attachmentStorage.importAttachment(it) } }
private fun loadContractsIntoAttachmentStore(attachmentStorage: AttachmentStorage): Map<SecureHash, URL> {
val cordappsWithAttachments = cordapps.filter { !it.contractClassNames.isEmpty() }.map { it.jarPath }
val attachmentIds = cordappsWithAttachments.map { it.openStream().use { attachmentStorage.importAttachment(it) } }
return attachmentIds.zip(cordappsWithAttachments).toMap()
}

View File

@ -0,0 +1,8 @@
package net.corda.node.internal.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappProvider
interface CordappProviderInternal : CordappProvider {
val cordapps: List<Cordapp>
}

View File

@ -30,11 +30,5 @@ interface SchemaService {
* or via custom logic in this service.
*/
fun generateMappedObject(state: ContractState, schema: MappedSchema): PersistentState
/**
* Registration mechanism to add custom contract schemas that extend the [MappedSchema] class.
*/
fun registerCustomSchemas(customSchemas: Set<MappedSchema>)
}
//DOCEND SchemaService

View File

@ -21,6 +21,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.loggerFor
import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.internal.cordapp.CordappProviderInternal
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
@ -89,7 +90,7 @@ interface ServiceHubInternal : ServiceHub {
val networkService: MessagingService
val database: CordaPersistence
val configuration: NodeConfiguration
override val cordappProvider: CordappProviderInternal
override fun recordTransactions(notifyVault: Boolean, txs: Iterable<SignedTransaction>) {
require(txs.any()) { "No transactions passed in for recording" }
val recordedTransactions = txs.filter { validatedTransactions.addTransaction(it) }

View File

@ -33,25 +33,13 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
private val sessionFactories = ConcurrentHashMap<Set<MappedSchema>, SessionFactory>()
private val transactionIsolationLevel = parserTransactionIsolationLevel(databaseProperties.getProperty("transactionIsolationLevel") ?: "")
init {
logger.info("Init HibernateConfiguration for schemas: ${schemaService.schemaOptions.keys}")
sessionFactoryForRegisteredSchemas()
val sessionFactoryForRegisteredSchemas = schemaService.schemaOptions.keys.let {
logger.info("Init HibernateConfiguration for schemas: $it")
sessionFactoryForSchemas(it)
}
fun sessionFactoryForRegisteredSchemas(): SessionFactory {
return sessionFactoryForSchemas(*schemaService.schemaOptions.keys.toTypedArray())
}
fun sessionFactoryForSchema(schema: MappedSchema): SessionFactory {
return sessionFactoryForSchemas(schema)
}
//vararg to set conversions left to preserve method signature for now
fun sessionFactoryForSchemas(vararg schemas: MappedSchema): SessionFactory {
val schemaSet: Set<MappedSchema> = schemas.toSet()
return sessionFactories.computeIfAbsent(schemaSet, { makeSessionFactoryForSchemas(schemaSet) })
}
/** @param key must be immutable, not just read-only. */
fun sessionFactoryForSchemas(key: Set<MappedSchema>) = sessionFactories.computeIfAbsent(key, { makeSessionFactoryForSchemas(key) })
private fun makeSessionFactoryForSchemas(schemas: Set<MappedSchema>): SessionFactory {
logger.info("Creating session factory for schemas: $schemas")

View File

@ -38,7 +38,7 @@ class HibernateObserver(vaultUpdates: Observable<Vault.Update<ContractState>>, v
}
fun persistStateWithSchema(state: ContractState, stateRef: StateRef, schema: MappedSchema) {
val sessionFactory = config.sessionFactoryForSchema(schema)
val sessionFactory = config.sessionFactoryForSchemas(setOf(schema))
val session = sessionFactory.withOptions().
connection(DatabaseTransactionManager.current().connection).
flushMode(FlushMode.MANUAL).

View File

@ -9,6 +9,7 @@ import net.corda.core.schemas.NodeInfoSchemaV1
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.api.SchemaService
import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.identity.PersistentIdentityService
@ -27,13 +28,13 @@ import net.corda.node.services.vault.VaultSchemaV1
/**
* Most basic implementation of [SchemaService].
*
* @param cordappLoader if not null, custom schemas will be extracted from its cordapps.
* TODO: support loading schema options from node configuration.
* TODO: support configuring what schemas are to be selected for persistence.
* TODO: support plugins for schema version upgrading or custom mapping not supported by original [QueryableState].
* TODO: create whitelisted tables when a CorDapp is first installed
*/
class NodeSchemaService(customSchemas: Set<MappedSchema> = emptySet()) : SchemaService, SingletonSerializeAsToken() {
class NodeSchemaService(cordappLoader: CordappLoader?) : SchemaService, SingletonSerializeAsToken() {
// Entities for compulsory services
object NodeServices
@ -67,9 +68,12 @@ class NodeSchemaService(customSchemas: Set<MappedSchema> = emptySet()) : SchemaS
Pair(NodeInfoSchemaV1, SchemaService.SchemaOptions()),
Pair(NodeServicesV1, SchemaService.SchemaOptions()))
override var schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = requiredSchemas.plus(customSchemas.map { mappedSchema ->
Pair(mappedSchema, SchemaService.SchemaOptions())
})
override val schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = if (cordappLoader == null) {
requiredSchemas
} else {
val customSchemas = cordappLoader.cordapps.flatMap { it.customSchemas }.toSet()
requiredSchemas.plus(customSchemas.map { mappedSchema -> Pair(mappedSchema, SchemaService.SchemaOptions()) })
}
// Currently returns all schemas supported by the state, with no filtering or enrichment.
override fun selectSchemas(state: ContractState): Iterable<MappedSchema> {
@ -92,10 +96,4 @@ class NodeSchemaService(customSchemas: Set<MappedSchema> = emptySet()) : SchemaS
return VaultSchemaV1.VaultFungibleStates(state.owner, state.amount.quantity, state.amount.token.issuer.party, state.amount.token.issuer.reference, state.participants)
return (state as QueryableState).generateMappedObject(schema)
}
override fun registerCustomSchemas(_customSchemas: Set<MappedSchema>) {
schemaOptions = schemaOptions.plus(_customSchemas.map { mappedSchema ->
Pair(mappedSchema, SchemaService.SchemaOptions())
})
}
}

View File

@ -59,7 +59,7 @@ private fun CriteriaBuilder.executeUpdate(session: Session, configure: Root<*>.(
* TODO: keep an audit trail with time stamps of previously unconsumed states "as of" a particular point in time.
* TODO: have transaction storage do some caching.
*/
class NodeVaultService(private val clock: Clock, private val keyManagementService: KeyManagementService, private val stateLoader: StateLoader, private val hibernateConfig: HibernateConfiguration) : SingletonSerializeAsToken(), VaultServiceInternal {
class NodeVaultService(private val clock: Clock, private val keyManagementService: KeyManagementService, private val stateLoader: StateLoader, hibernateConfig: HibernateConfiguration) : SingletonSerializeAsToken(), VaultServiceInternal {
private companion object {
val log = loggerFor<NodeVaultService>()
@ -377,9 +377,8 @@ class NodeVaultService(private val clock: Clock, private val keyManagementServic
return keysToCheck.any { it in myKeys }
}
private var sessionFactory = hibernateConfig.sessionFactoryForRegisteredSchemas()
private var criteriaBuilder = sessionFactory.criteriaBuilder
private val sessionFactory = hibernateConfig.sessionFactoryForRegisteredSchemas
private val criteriaBuilder = sessionFactory.criteriaBuilder
/**
* Maintain a list of contract state interfaces to concrete types stored in the vault
* for usage in generic queries of type queryBy<LinearState> or queryBy<FungibleState<*>>
@ -406,11 +405,6 @@ class NodeVaultService(private val clock: Clock, private val keyManagementServic
@Throws(VaultQueryException::class)
override fun <T : ContractState> _queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): Vault.Page<T> {
log.info("Vault Query for contract type: $contractStateType, criteria: $criteria, pagination: $paging, sorting: $sorting")
// refresh to include any schemas registered after initial VQ service initialisation
sessionFactory = hibernateConfig.sessionFactoryForRegisteredSchemas()
criteriaBuilder = sessionFactory.criteriaBuilder
// calculate total results where a page specification has been defined
var totalStates = -1L
if (!paging.isDefault) {

View File

@ -6,8 +6,6 @@ import net.corda.core.node.services.IdentityService
import net.corda.node.services.api.SchemaService
import net.corda.node.services.persistence.HibernateConfiguration
import net.corda.node.services.schema.NodeSchemaService
import org.hibernate.SessionFactory
import rx.Observable
import rx.Subscriber
import rx.subjects.UnicastSubject
@ -32,12 +30,7 @@ class CordaPersistence(var dataSource: HikariDataSource, private val schemaServi
HibernateConfiguration(schemaService, databaseProperties, createIdentityService)
}
}
val entityManagerFactory: SessionFactory by lazy {
transaction {
hibernateConfig.sessionFactoryForRegisteredSchemas()
}
}
val entityManagerFactory get() = hibernateConfig.sessionFactoryForRegisteredSchemas
companion object {
fun connect(dataSource: HikariDataSource, schemaService: SchemaService, createIdentityService: () -> IdentityService, databaseProperties: Properties): CordaPersistence {
@ -103,7 +96,7 @@ class CordaPersistence(var dataSource: HikariDataSource, private val schemaServi
}
}
fun configureDatabase(dataSourceProperties: Properties, databaseProperties: Properties?, schemaService: SchemaService = NodeSchemaService(), createIdentityService: () -> IdentityService): CordaPersistence {
fun configureDatabase(dataSourceProperties: Properties, databaseProperties: Properties?, createIdentityService: () -> IdentityService, schemaService: SchemaService = NodeSchemaService(null)): CordaPersistence {
val config = HikariConfig(dataSourceProperties)
val dataSource = HikariDataSource(config)
val persistence = CordaPersistence.connect(dataSource, schemaService, createIdentityService, databaseProperties ?: Properties())

View File

@ -8,7 +8,6 @@ import net.corda.core.messaging.*;
import net.corda.core.node.services.*;
import net.corda.core.node.services.vault.*;
import net.corda.core.node.services.vault.QueryCriteria.*;
import net.corda.core.schemas.*;
import net.corda.core.utilities.*;
import net.corda.finance.contracts.*;
import net.corda.finance.contracts.asset.*;
@ -43,14 +42,13 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
@Before
public void setUp() {
List<String> cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset");
List<String> cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName());
ArrayList<KeyPair> keys = new ArrayList<>();
keys.add(getMEGA_CORP_KEY());
keys.add(getDUMMY_NOTARY_KEY());
Set<MappedSchema> requiredSchemas = Collections.singleton(CashSchemaV1.INSTANCE);
IdentityService identitySvc = makeTestIdentityService();
@SuppressWarnings("unchecked")
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc, cordappPackages);
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(keys, () -> identitySvc, cordappPackages);
issuerServices = new MockServices(cordappPackages, getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY());
database = databaseAndServices.getFirst();
services = databaseAndServices.getSecond();

View File

@ -26,7 +26,7 @@ import kotlin.test.assertEquals
class InteractiveShellTest {
@Before
fun setup() {
InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), MockServices.makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), MockServices.makeTestDatabaseProperties(), ::makeTestIdentityService)
}
@After

View File

@ -1,5 +1,6 @@
package net.corda.node.internal.cordapp
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.node.services.AttachmentStorage
import net.corda.testing.node.MockAttachmentStorage
import org.junit.Assert
@ -22,9 +23,7 @@ class CordappProviderImplTests {
@Test
fun `isolated jar is loaded into the attachment store`() {
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
val provider = CordappProviderImpl(loader)
provider.start(attachmentStore)
val provider = CordappProviderImpl(loader, attachmentStore)
val maybeAttachmentId = provider.getCordappAttachmentId(provider.cordapps.first())
Assert.assertNotNull(maybeAttachmentId)
@ -34,17 +33,14 @@ class CordappProviderImplTests {
@Test
fun `empty jar is not loaded into the attachment store`() {
val loader = CordappLoader.createDevMode(listOf(emptyJAR))
val provider = CordappProviderImpl(loader)
provider.start(attachmentStore)
val provider = CordappProviderImpl(loader, attachmentStore)
Assert.assertNull(provider.getCordappAttachmentId(provider.cordapps.first()))
}
@Test
fun `test that we find a cordapp class that is loaded into the store`() {
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
val provider = CordappProviderImpl(loader)
val provider = CordappProviderImpl(loader, mock())
val className = "net.corda.finance.contracts.isolated.AnotherDummyContract"
val expected = provider.cordapps.first()
@ -57,10 +53,8 @@ class CordappProviderImplTests {
@Test
fun `test that we find an attachment for a cordapp contrat class`() {
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
val provider = CordappProviderImpl(loader)
val provider = CordappProviderImpl(loader, attachmentStore)
val className = "net.corda.finance.contracts.isolated.AnotherDummyContract"
provider.start(attachmentStore)
val expected = provider.getAppContext(provider.cordapps.first()).attachmentId
val actual = provider.getContractAttachmentID(className)

View File

@ -78,7 +78,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
calls = 0
val dataSourceProps = makeTestDataSourceProperties()
val databaseProperties = makeTestDatabaseProperties()
database = configureDatabase(dataSourceProps, databaseProperties, createIdentityService = ::makeTestIdentityService)
database = configureDatabase(dataSourceProps, databaseProperties, ::makeTestIdentityService)
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
val kms = MockKeyManagementService(identityService, ALICE_KEY)
@ -97,7 +97,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
network = mockMessagingService), TestReference {
override val vaultService: VaultServiceInternal = NodeVaultService(testClock, kms, stateLoader, database.hibernateConfig)
override val testReference = this@NodeSchedulerServiceTest
override val cordappProvider = CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.testing.contracts"))).start(attachments)
override val cordappProvider = CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.testing.contracts")), attachments)
}
smmExecutor = AffinityExecutor.ServiceAffinityExecutor("test", 1)
scheduler = NodeSchedulerService(services, schedulerGatedExecutor, serverThread = smmExecutor)

View File

@ -71,7 +71,7 @@ class ArtemisMessagingTests : TestDependencyInjectionBase() {
baseDirectory = baseDirectory,
myLegalName = ALICE.name)
LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
networkMapRegistrationFuture = doneFuture(Unit)
networkMapCache = PersistentNetworkMapCache(serviceHub = object : MockServiceHubInternal(database, config) {})
}

View File

@ -33,7 +33,7 @@ class DBCheckpointStorageTests : TestDependencyInjectionBase() {
@Before
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
newCheckpointStorage()
}

View File

@ -38,7 +38,7 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() {
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
val dataSourceProps = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(), NodeSchemaService(), ::makeTestIdentityService)
database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(), ::makeTestIdentityService)
database.transaction {
services = object : MockServices(BOB_KEY) {

View File

@ -9,6 +9,7 @@ import net.corda.core.utilities.toBase58String
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService
import net.corda.core.schemas.CommonSchemaV1
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentStateRef
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.deserialize
@ -25,7 +26,6 @@ import net.corda.finance.schemas.SampleCashSchemaV2
import net.corda.finance.schemas.SampleCashSchemaV3
import net.corda.finance.utils.sumCash
import net.corda.node.services.schema.HibernateObserver
import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.services.vault.VaultSchemaV1
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase
@ -77,7 +77,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_KEY, BOB_KEY, BOC_KEY)
val dataSourceProps = makeTestDataSourceProperties()
val defaultDatabaseProperties = makeTestDatabaseProperties()
database = configureDatabase(dataSourceProps, defaultDatabaseProperties, NodeSchemaService(), ::makeTestIdentityService)
database = configureDatabase(dataSourceProps, defaultDatabaseProperties, ::makeTestIdentityService)
database.transaction {
hibernateConfig = database.hibernateConfig
services = object : MockServices(cordappPackages, BOB_KEY, BOC_KEY, DUMMY_NOTARY_KEY) {
@ -95,13 +95,13 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
hibernatePersister = services.hibernatePersister
}
setUpDb()
val customSchemas = setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3)
sessionFactory = hibernateConfig.sessionFactoryForSchemas(*customSchemas.toTypedArray())
sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3)
entityManager = sessionFactory.createEntityManager()
criteriaBuilder = sessionFactory.criteriaBuilder
}
private fun sessionFactoryForSchemas(vararg schemas: MappedSchema) = hibernateConfig.sessionFactoryForSchemas(schemas.toSet())
@After
fun cleanUp() {
database.close()
@ -536,8 +536,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
services.fillWithSomeTestLinearStates(2)
}
val sessionFactory = hibernateConfig.sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV1)
val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV1)
val criteriaBuilder = sessionFactory.criteriaBuilder
val entityManager = sessionFactory.createEntityManager()
@ -568,8 +567,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
services.fillWithSomeTestLinearStates(2)
}
val sessionFactory = hibernateConfig.sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV2)
val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV2)
val criteriaBuilder = sessionFactory.criteriaBuilder
val entityManager = sessionFactory.createEntityManager()
@ -635,8 +633,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
}
}
val sessionFactory = hibernateConfig.sessionFactoryForSchemas(VaultSchemaV1, CommonSchemaV1, SampleCashSchemaV3)
val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, CommonSchemaV1, SampleCashSchemaV3)
val criteriaBuilder = sessionFactory.criteriaBuilder
val entityManager = sessionFactory.createEntityManager()
@ -764,8 +761,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
services.fillWithSomeTestLinearStates(2, externalId = "222")
services.fillWithSomeTestLinearStates(3, externalId = "333")
}
val sessionFactory = hibernateConfig.sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV2)
val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV2)
val criteriaBuilder = sessionFactory.criteriaBuilder
val entityManager = sessionFactory.createEntityManager()
@ -817,8 +813,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
services.fillWithSomeTestLinearStates(2, externalId = "222")
services.fillWithSomeTestLinearStates(3, externalId = "333")
}
val sessionFactory = hibernateConfig.sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV1)
val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV1)
val criteriaBuilder = sessionFactory.criteriaBuilder
val entityManager = sessionFactory.createEntityManager()

View File

@ -40,7 +40,7 @@ class NodeAttachmentStorageTest {
LogHelper.setLevel(PersistentUniquenessProvider::class)
val dataSourceProperties = makeTestDataSourceProperties()
database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
database = configureDatabase(dataSourceProperties, makeTestDatabaseProperties(), ::makeTestIdentityService)
fs = Jimfs.newFileSystem(Configuration.unix())
}

View File

@ -57,8 +57,6 @@ class HibernateObserverTests {
val testSchema = TestSchema
val rawUpdatesPublisher = PublishSubject.create<Vault.Update<ContractState>>()
val schemaService = object : SchemaService {
override fun registerCustomSchemas(customSchemas: Set<MappedSchema>) {}
override val schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = emptyMap()
override fun selectSchemas(state: ContractState): Iterable<MappedSchema> = setOf(testSchema)
@ -70,7 +68,7 @@ class HibernateObserverTests {
return parent
}
}
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), schemaService, createIdentityService = ::makeTestIdentityService)
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService, schemaService)
@Suppress("UNUSED_VARIABLE")
val observer = HibernateObserver(rawUpdatesPublisher, database.hibernateConfig)
database.transaction {

View File

@ -3,11 +3,13 @@ package net.corda.node.services.schema
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC
import net.corda.core.internal.packageName
import net.corda.core.messaging.startFlow
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.utilities.getOrThrow
import net.corda.node.services.api.ServiceHubInternal
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver
import net.corda.testing.node.MockNetwork
import net.corda.testing.schemas.DummyLinearStateSchemaV1
@ -15,6 +17,7 @@ import org.hibernate.annotations.Cascade
import org.hibernate.annotations.CascadeType
import org.junit.Test
import javax.persistence.*
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class NodeSchemaServiceTest {
@ -23,11 +26,9 @@ class NodeSchemaServiceTest {
*/
@Test
fun `registering custom schemas for testing with MockNode`() {
val mockNet = MockNetwork()
val mockNet = MockNetwork(cordappPackages = listOf(DummyLinearStateSchemaV1::class.packageName))
val mockNode = mockNet.createNode()
mockNet.runNetwork()
mockNode.internals.registerCustomSchemas(setOf(DummyLinearStateSchemaV1))
val schemaService = mockNode.services.schemaService
assertTrue(schemaService.schemaOptions.containsKey(DummyLinearStateSchemaV1))
@ -50,6 +51,16 @@ class NodeSchemaServiceTest {
}
}
@Test
fun `custom schemas are loaded eagerly`() {
val expected = setOf("PARENTS", "CHILDREN")
assertEquals<Set<*>>(expected, driver {
(startNode(startInSameProcess = true).getOrThrow() as NodeHandle.InProcess).node.database.transaction {
session.createNativeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES").list()
}
}.toMutableSet().apply { retainAll(expected) })
}
@StartableByRPC
class MappedSchemasFlow : FlowLogic<List<String>>() {
@Suspendable

View File

@ -86,7 +86,7 @@ class DistributedImmutableMapTests : TestDependencyInjectionBase() {
private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture<Member> {
val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build()
val address = Address(myAddress.host, myAddress.port)
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties("serverNameTablePrefix", "PORT_${myAddress.port}_"), createIdentityService = ::makeTestIdentityService)
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties("serverNameTablePrefix", "PORT_${myAddress.port}_"), ::makeTestIdentityService)
databases.add(database)
val stateMachineFactory = { DistributedImmutableMap(database, RaftUniquenessProvider.Companion::createMap) }

View File

@ -23,7 +23,7 @@ class PersistentUniquenessProviderTests : TestDependencyInjectionBase() {
@Before
fun setUp() {
LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
}
@After

View File

@ -9,6 +9,7 @@ import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.internal.packageName
import net.corda.core.node.services.*
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.QueryCriteria.*
@ -46,7 +47,7 @@ import kotlin.test.assertTrue
class NodeVaultServiceTest : TestDependencyInjectionBase() {
companion object {
private val cordappPackages = listOf("net.corda.finance.contracts.asset")
private val cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)
}
lateinit var services: MockServices
@ -58,7 +59,6 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
fun setUp() {
LogHelper.setLevel(NodeVaultService::class)
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(BOC_KEY, DUMMY_CASH_ISSUER_KEY),
customSchemas = setOf(CashSchemaV1),
cordappPackages = cordappPackages)
database = databaseAndServices.first
services = databaseAndServices.second

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.packageName
import net.corda.core.node.services.*
import net.corda.core.node.services.vault.*
import net.corda.core.node.services.vault.QueryCriteria.*
@ -45,10 +46,9 @@ import java.time.temporal.ChronoUnit
import java.util.*
class VaultQueryTests : TestDependencyInjectionBase() {
companion object {
private val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts")
}
private val cordappPackages = setOf(
"net.corda.testing.contracts", "net.corda.finance.contracts",
CashSchemaV1::class.packageName, CommercialPaperSchemaV1::class.packageName, DummyLinearStateSchemaV1::class.packageName).toMutableList()
private lateinit var services: MockServices
private lateinit var notaryServices: MockServices
private val vaultService: VaultService get() = services.vaultService
@ -67,7 +67,6 @@ class VaultQueryTests : TestDependencyInjectionBase() {
identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY)
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY),
createIdentityService = { identitySvc },
customSchemas = setOf(CashSchemaV1, CommercialPaperSchemaV1, DummyLinearStateSchemaV1),
cordappPackages = cordappPackages)
database = databaseAndServices.first
services = databaseAndServices.second
@ -85,8 +84,7 @@ class VaultQueryTests : TestDependencyInjectionBase() {
@Ignore
@Test
fun createPersistentTestDb() {
val database = configureDatabase(makePersistentDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = { identitySvc })
val database = configureDatabase(makePersistentDataSourceProperties(), makeTestDatabaseProperties(), { identitySvc })
setUpDb(database, 5000)
database.close()
@ -1753,6 +1751,9 @@ class VaultQueryTests : TestDependencyInjectionBase() {
@Test
fun `query attempting to use unregistered schema`() {
tearDown()
cordappPackages -= SampleCashSchemaV3::class.packageName
setUp()
database.transaction {
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L))
services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L))

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AnonymousParty
import net.corda.core.internal.packageName
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.queryBy
@ -35,7 +36,7 @@ import kotlin.test.assertEquals
class VaultWithCashTest : TestDependencyInjectionBase() {
companion object {
private val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset")
private val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)
}
lateinit var services: MockServices
@ -48,7 +49,6 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
fun setUp() {
LogHelper.setLevel(VaultWithCashTest::class)
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(DUMMY_CASH_ISSUER_KEY, DUMMY_NOTARY_KEY),
customSchemas = setOf(CashSchemaV1),
cordappPackages = cordappPackages)
database = databaseAndServices.first
services = databaseAndServices.second

View File

@ -21,7 +21,7 @@ class ObservablesTests {
val toBeClosed = mutableListOf<Closeable>()
fun createDatabase(): CordaPersistence {
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
val database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
toBeClosed += database
return database
}