mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
ENT-5258 db schema set-up only via command line flag (#6280)
Removing the ability to initialise schema from the node config, and add a new sub-command to initialise the schema (that does not do anything else and exits afterwards). Also adding a command line flag that allow app schema to be maintained by hibernate for legacy cordapps, tests or rapid development. Patching up mock net and driver test frameworks so they create the required schemas for tests to work, defaulting schema migration and hibernate schema management to true to match pre-existing behaviour. Modified network bootstrapper to run an initial schema set-up so it can register nodes.
This commit is contained in:
parent
8a0916b2a2
commit
70f1ea0a9d
@ -112,7 +112,7 @@ class FlowIsKilledTest {
|
||||
}
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `manually handle killed flows using checkFlowIsNotKilled`() {
|
||||
fun `manually handle killed flows using checkForIsNotKilled`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
alice.rpc.let { rpc ->
|
||||
@ -131,7 +131,7 @@ class FlowIsKilledTest {
|
||||
}
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `manually handle killed flows using checkFlowIsNotKilled with lazy message`() {
|
||||
fun `manually handle killed flows using checkForIsNotKilled with lazy message`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
alice.rpc.let { rpc ->
|
||||
|
@ -2,12 +2,11 @@ package net.corda.nodeapitests.internal.persistence
|
||||
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.MissingMigrationException
|
||||
import net.corda.nodeapi.internal.persistence.SchemaMigration
|
||||
import net.corda.node.internal.DataSourceFactory
|
||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.nodeapi.internal.persistence.MissingMigrationException
|
||||
import net.corda.nodeapi.internal.persistence.SchemaMigration
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.node.MockServices
|
||||
@ -41,8 +40,7 @@ class MissingSchemaMigrationTest {
|
||||
}
|
||||
|
||||
private fun createSchemaMigration(schemasToMigrate: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean): SchemaMigration {
|
||||
val databaseConfig = DatabaseConfig()
|
||||
return SchemaMigration(schemasToMigrate, dataSource, databaseConfig, null, null,
|
||||
return SchemaMigration(schemasToMigrate, dataSource, null, null,
|
||||
TestIdentity(ALICE_NAME, 70).name, forceThrowOnMissingMigration)
|
||||
}
|
||||
|
||||
@ -50,7 +48,7 @@ class MissingSchemaMigrationTest {
|
||||
fun `test that an error is thrown when forceThrowOnMissingMigration is set and a mapped schema is missing a migration`() {
|
||||
assertThatThrownBy {
|
||||
createSchemaMigration(setOf(GoodSchema), true)
|
||||
.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
.runMigration(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
}.isInstanceOf(MissingMigrationException::class.java)
|
||||
}
|
||||
|
||||
@ -58,7 +56,7 @@ class MissingSchemaMigrationTest {
|
||||
fun `test that an error is not thrown when forceThrowOnMissingMigration is not set and a mapped schema is missing a migration`() {
|
||||
assertDoesNotThrow {
|
||||
createSchemaMigration(setOf(GoodSchema), false)
|
||||
.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
.runMigration(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +65,7 @@ class MissingSchemaMigrationTest {
|
||||
assertDoesNotThrow("This test failure indicates " +
|
||||
"a new table has been added to the node without the appropriate migration scripts being present") {
|
||||
createSchemaMigration(NodeSchemaService().internalSchemas(), false)
|
||||
.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
.runMigration(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,13 @@ constructor(private val initSerEnv: Boolean,
|
||||
"generate-node-info"
|
||||
)
|
||||
|
||||
private val createSchemasCmd = listOf(
|
||||
Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
|
||||
"-jar",
|
||||
"corda.jar",
|
||||
"run-migration-scripts"
|
||||
)
|
||||
|
||||
private const val LOGS_DIR_NAME = "logs"
|
||||
|
||||
private val jarsThatArentCordapps = setOf("corda.jar", "runnodes.jar")
|
||||
@ -92,7 +99,9 @@ constructor(private val initSerEnv: Boolean,
|
||||
}
|
||||
val executor = Executors.newFixedThreadPool(numParallelProcesses)
|
||||
return try {
|
||||
nodeDirs.map { executor.fork { generateNodeInfo(it) } }.transpose().getOrThrow()
|
||||
nodeDirs.map { executor.fork {
|
||||
createDbSchemas(it)
|
||||
generateNodeInfo(it) } }.transpose().getOrThrow()
|
||||
} finally {
|
||||
warningTimer.cancel()
|
||||
executor.shutdownNow()
|
||||
@ -100,23 +109,31 @@ constructor(private val initSerEnv: Boolean,
|
||||
}
|
||||
|
||||
private fun generateNodeInfo(nodeDir: Path): Path {
|
||||
runNodeJob(nodeInfoGenCmd, nodeDir, "node-info-gen.log")
|
||||
return nodeDir.list { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDbSchemas(nodeDir: Path) {
|
||||
runNodeJob(createSchemasCmd, nodeDir, "node-run-migration.log")
|
||||
}
|
||||
|
||||
private fun runNodeJob(command: List<String>, nodeDir: Path, logfileName: String) {
|
||||
val logsDir = (nodeDir / LOGS_DIR_NAME).createDirectories()
|
||||
val nodeInfoGenFile = (logsDir / "node-info-gen.log").toFile()
|
||||
val process = ProcessBuilder(nodeInfoGenCmd)
|
||||
val nodeRedirectFile = (logsDir / logfileName).toFile()
|
||||
val process = ProcessBuilder(command)
|
||||
.directory(nodeDir.toFile())
|
||||
.redirectErrorStream(true)
|
||||
.redirectOutput(nodeInfoGenFile)
|
||||
.redirectOutput(nodeRedirectFile)
|
||||
.apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" }
|
||||
.start()
|
||||
try {
|
||||
if (!process.waitFor(3, TimeUnit.MINUTES)) {
|
||||
process.destroyForcibly()
|
||||
printNodeInfoGenLogToConsole(nodeInfoGenFile)
|
||||
}
|
||||
printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 }
|
||||
return nodeDir.list { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
||||
printNodeOutputToConsoleAndThrow(nodeRedirectFile)
|
||||
}
|
||||
if (process.exitValue() != 0) printNodeOutputToConsoleAndThrow(nodeRedirectFile)
|
||||
} catch (e: InterruptedException) {
|
||||
// Don't leave this process dangling if the thread is interrupted.
|
||||
process.destroyForcibly()
|
||||
@ -124,18 +141,16 @@ constructor(private val initSerEnv: Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
private fun printNodeInfoGenLogToConsole(nodeInfoGenFile: File, check: (() -> Boolean) = { true }) {
|
||||
if (!check.invoke()) {
|
||||
val nodeDir = nodeInfoGenFile.parent
|
||||
val nodeIdentifier = try {
|
||||
ConfigFactory.parseFile((nodeDir / "node.conf").toFile()).getString("myLegalName")
|
||||
} catch (e: ConfigException) {
|
||||
nodeDir
|
||||
}
|
||||
System.err.println("#### Error while generating node info file $nodeIdentifier ####")
|
||||
nodeInfoGenFile.inputStream().copyTo(System.err)
|
||||
throw IllegalStateException("Error while generating node info file. Please check the logs in $nodeDir.")
|
||||
private fun printNodeOutputToConsoleAndThrow(stdoutFile: File) {
|
||||
val nodeDir = stdoutFile.parent
|
||||
val nodeIdentifier = try {
|
||||
ConfigFactory.parseFile((nodeDir / "node.conf").toFile()).getString("myLegalName")
|
||||
} catch (e: ConfigException) {
|
||||
nodeDir
|
||||
}
|
||||
System.err.println("#### Error while generating node info file $nodeIdentifier ####")
|
||||
stdoutFile.inputStream().copyTo(System.err)
|
||||
throw IllegalStateException("Error while generating node info file. Please check the logs in $nodeDir.")
|
||||
}
|
||||
|
||||
const val DEFAULT_MAX_MESSAGE_SIZE: Int = 10485760
|
||||
|
@ -31,24 +31,12 @@ import javax.sql.DataSource
|
||||
*/
|
||||
const val NODE_DATABASE_PREFIX = "node_"
|
||||
|
||||
enum class SchemaInitializationType{
|
||||
NONE,
|
||||
VALIDATE,
|
||||
UPDATE
|
||||
}
|
||||
|
||||
// This class forms part of the node config and so any changes to it must be handled with care
|
||||
data class DatabaseConfig(
|
||||
val initialiseSchema: Boolean = Defaults.initialiseSchema,
|
||||
val initialiseAppSchema: SchemaInitializationType = Defaults.initialiseAppSchema,
|
||||
val transactionIsolationLevel: TransactionIsolationLevel = Defaults.transactionIsolationLevel,
|
||||
val exportHibernateJMXStatistics: Boolean = Defaults.exportHibernateJMXStatistics,
|
||||
val mappedSchemaCacheSize: Long = Defaults.mappedSchemaCacheSize
|
||||
) {
|
||||
object Defaults {
|
||||
val initialiseSchema = true
|
||||
val initialiseAppSchema = SchemaInitializationType.UPDATE
|
||||
val transactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
||||
val exportHibernateJMXStatistics = false
|
||||
val mappedSchemaCacheSize = 100L
|
||||
}
|
||||
@ -67,6 +55,10 @@ enum class TransactionIsolationLevel {
|
||||
*/
|
||||
val jdbcString = "TRANSACTION_$name"
|
||||
val jdbcValue: Int = java.sql.Connection::class.java.getField(jdbcString).get(null) as Int
|
||||
|
||||
companion object{
|
||||
val default = REPEATABLE_READ
|
||||
}
|
||||
}
|
||||
|
||||
internal val _prohibitDatabaseAccess = ThreadLocal.withInitial { false }
|
||||
@ -103,20 +95,21 @@ class CordaPersistence(
|
||||
attributeConverters: Collection<AttributeConverter<*, *>> = emptySet(),
|
||||
customClassLoader: ClassLoader? = null,
|
||||
val closeConnection: Boolean = true,
|
||||
val errorHandler: DatabaseTransaction.(e: Exception) -> Unit = {}
|
||||
val errorHandler: DatabaseTransaction.(e: Exception) -> Unit = {},
|
||||
allowHibernateToManageAppSchema: Boolean = false
|
||||
) : Closeable {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
|
||||
private val defaultIsolationLevel = databaseConfig.transactionIsolationLevel
|
||||
private val defaultIsolationLevel = TransactionIsolationLevel.default
|
||||
val hibernateConfig: HibernateConfiguration by lazy {
|
||||
transaction {
|
||||
try {
|
||||
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader)
|
||||
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader, allowHibernateToManageAppSchema)
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is SchemaManagementException -> throw HibernateSchemaChangeException("Incompatible schema change detected. Please run the node with database.initialiseSchema=true. Reason: ${e.message}", e)
|
||||
is SchemaManagementException -> throw HibernateSchemaChangeException("Incompatible schema change detected. Please run schema migration scripts (node with sub-command run-migration-scripts). Reason: ${e.message}", e)
|
||||
else -> throw HibernateConfigException("Could not create Hibernate configuration: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ class HibernateConfiguration(
|
||||
private val attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
jdbcUrl: String,
|
||||
cacheFactory: NamedCacheFactory,
|
||||
val customClassLoader: ClassLoader? = null
|
||||
val customClassLoader: ClassLoader? = null,
|
||||
val allowHibernateToManageAppSchema: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
@ -64,7 +65,7 @@ class HibernateConfiguration(
|
||||
fun sessionFactoryForSchemas(key: Set<MappedSchema>): SessionFactory = sessionFactories.get(key, ::makeSessionFactoryForSchemas)!!
|
||||
|
||||
private fun makeSessionFactoryForSchemas(schemas: Set<MappedSchema>): SessionFactory {
|
||||
val sessionFactory = sessionFactoryFactory.makeSessionFactoryForSchemas(databaseConfig, schemas, customClassLoader, attributeConverters)
|
||||
val sessionFactory = sessionFactoryFactory.makeSessionFactoryForSchemas(databaseConfig, schemas, customClassLoader, attributeConverters, allowHibernateToManageAppSchema)
|
||||
|
||||
// export Hibernate JMX statistics
|
||||
if (databaseConfig.exportHibernateJMXStatistics)
|
||||
|
@ -25,7 +25,6 @@ import kotlin.concurrent.withLock
|
||||
class SchemaMigration(
|
||||
val schemas: Set<MappedSchema>,
|
||||
val dataSource: DataSource,
|
||||
private val databaseConfig: DatabaseConfig,
|
||||
cordappLoader: CordappLoader? = null,
|
||||
private val currentDirectory: Path?,
|
||||
// This parameter is used by the vault state migration to establish what the node's legal identity is when setting up
|
||||
@ -50,29 +49,18 @@ class SchemaMigration(
|
||||
|
||||
private val classLoader = cordappLoader?.appClassLoader ?: Thread.currentThread().contextClassLoader
|
||||
|
||||
/**
|
||||
* Main entry point to the schema migration.
|
||||
* Called during node startup.
|
||||
*/
|
||||
fun nodeStartup(existingCheckpoints: Boolean) {
|
||||
when {
|
||||
databaseConfig.initialiseSchema -> {
|
||||
migrateOlderDatabaseToUseLiquibase(existingCheckpoints)
|
||||
runMigration(existingCheckpoints)
|
||||
}
|
||||
else -> checkState()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will run the Liquibase migration on the actual database.
|
||||
*/
|
||||
private fun runMigration(existingCheckpoints: Boolean) = doRunMigration(run = true, check = false, existingCheckpoints = existingCheckpoints)
|
||||
fun runMigration(existingCheckpoints: Boolean) {
|
||||
migrateOlderDatabaseToUseLiquibase(existingCheckpoints)
|
||||
doRunMigration(run = true, check = false, existingCheckpoints = existingCheckpoints)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the database is up to date with the latest migration changes.
|
||||
*/
|
||||
private fun checkState() = doRunMigration(run = false, check = true)
|
||||
fun checkState() = doRunMigration(run = false, check = true)
|
||||
|
||||
/** Create a resourse accessor that aggregates the changelogs included in the schemas into one dynamic stream. */
|
||||
private class CustomResourceAccessor(val dynamicInclude: String, val changelogList: List<String?>, classLoader: ClassLoader) : ClassLoaderResourceAccessor(classLoader) {
|
||||
@ -269,6 +257,6 @@ class CheckpointsException : DatabaseMigrationException("Attempting to update th
|
||||
|
||||
class DatabaseIncompatibleException(@Suppress("MemberVisibilityCanBePrivate") private val reason: String) : DatabaseMigrationException(errorMessageFor(reason)) {
|
||||
internal companion object {
|
||||
fun errorMessageFor(reason: String): String = "Incompatible database schema version detected, please run the node with configuration option database.initialiseSchema=true. Reason: $reason"
|
||||
fun errorMessageFor(reason: String): String = "Incompatible database schema version detected, please run schema migration scripts (node with sub-command run-migration-scripts). Reason: $reason"
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
import net.corda.nodeapi.internal.persistence.SchemaInitializationType
|
||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||
import org.hibernate.SessionFactory
|
||||
import org.hibernate.boot.Metadata
|
||||
import org.hibernate.boot.MetadataBuilder
|
||||
@ -26,22 +26,19 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
open fun buildHibernateConfig(databaseConfig: DatabaseConfig, metadataSources: MetadataSources): Configuration {
|
||||
open fun buildHibernateConfig(databaseConfig: DatabaseConfig, metadataSources: MetadataSources, allowHibernateToManageAppSchema: Boolean): Configuration {
|
||||
val hbm2dll: String =
|
||||
if (databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE) {
|
||||
if (allowHibernateToManageAppSchema) {
|
||||
"update"
|
||||
} else if ((!databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE)
|
||||
|| databaseConfig.initialiseAppSchema == SchemaInitializationType.VALIDATE) {
|
||||
} else {
|
||||
"validate"
|
||||
} else {
|
||||
"none"
|
||||
}
|
||||
// We set a connection provider as the auto schema generation requires it. The auto schema generation will not
|
||||
// necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though.
|
||||
return Configuration(metadataSources).setProperty("hibernate.connection.provider_class", HibernateConfiguration.NodeDatabaseConnectionProvider::class.java.name)
|
||||
.setProperty("hibernate.format_sql", "true")
|
||||
.setProperty("javax.persistence.validation.mode", "none")
|
||||
.setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString())
|
||||
.setProperty("hibernate.connection.isolation", TransactionIsolationLevel.default.jdbcValue.toString())
|
||||
.setProperty("hibernate.hbm2ddl.auto", hbm2dll)
|
||||
.setProperty("hibernate.jdbc.time_zone", "UTC")
|
||||
}
|
||||
@ -88,12 +85,13 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
||||
databaseConfig: DatabaseConfig,
|
||||
schemas: Set<MappedSchema>,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory {
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
allowHibernateToMananageAppSchema: Boolean): SessionFactory {
|
||||
logger.info("Creating session factory for schemas: $schemas")
|
||||
val serviceRegistry = BootstrapServiceRegistryBuilder().build()
|
||||
val metadataSources = MetadataSources(serviceRegistry)
|
||||
|
||||
val config = buildHibernateConfig(databaseConfig, metadataSources)
|
||||
val config = buildHibernateConfig(databaseConfig, metadataSources, allowHibernateToMananageAppSchema)
|
||||
schemas.forEach { schema ->
|
||||
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ interface CordaSessionFactoryFactory {
|
||||
databaseConfig: DatabaseConfig,
|
||||
schemas: Set<MappedSchema>,
|
||||
customClassLoader: ClassLoader?,
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory
|
||||
attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||
allowHibernateToMananageAppSchema: Boolean): SessionFactory
|
||||
fun getExtraConfiguration(key: String): Any?
|
||||
fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package net.corda.serialization.reproduction;
|
||||
|
||||
import com.google.common.io.LineProcessor;
|
||||
import net.corda.client.rpc.CordaRPCClient;
|
||||
import net.corda.core.concurrent.CordaFuture;
|
||||
import net.corda.node.services.Permissions;
|
||||
|
@ -44,7 +44,7 @@ class BootTests {
|
||||
rpc.startFlow(::ObjectInputStreamFlow).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
driver(DriverParameters(cordappsForAllNodes = listOf(enclosedCordapp()))) {
|
||||
driver(DriverParameters(cordappsForAllNodes = listOf(enclosedCordapp()), allowHibernateToManageAppSchema = false)) {
|
||||
val devModeNode = startNode(devParams).getOrThrow()
|
||||
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
||||
|
||||
|
@ -17,7 +17,7 @@ import javax.security.auth.x500.X500Principal
|
||||
class NodeKeystoreCheckTest {
|
||||
@Test(timeout=300_000)
|
||||
fun `starting node in non-dev mode with no key store`() {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = emptyList())) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = emptyList(), allowHibernateToManageAppSchema = false)) {
|
||||
assertThatThrownBy {
|
||||
startNode(customOverrides = mapOf("devMode" to false)).getOrThrow()
|
||||
}.hasMessageContaining("One or more keyStores (identity or TLS) or trustStore not found.")
|
||||
@ -26,7 +26,7 @@ class NodeKeystoreCheckTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `node should throw exception if cert path does not chain to the trust root`() {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = emptyList())) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = emptyList(), allowHibernateToManageAppSchema = false)) {
|
||||
// Create keystores.
|
||||
val keystorePassword = "password"
|
||||
val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
|
||||
|
@ -2,32 +2,21 @@ package net.corda.node.persistence
|
||||
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.flows.isQuasarAgentSpecified
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseIncompatibleException
|
||||
import net.corda.node.internal.ConfigurationException
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.NodeParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class DbSchemaInitialisationTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `database is initialised`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `database initialisation not allowed in config`() {
|
||||
driver(DriverParameters(startNodesInProcess = isQuasarAgentSpecified(), cordappsForAllNodes = emptyList())) {
|
||||
val nodeHandle = {
|
||||
startNode(NodeParameters(customOverrides = mapOf("database.initialiseSchema" to "true"))).getOrThrow()
|
||||
}()
|
||||
assertNotNull(nodeHandle)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `database is not initialised`() {
|
||||
driver(DriverParameters(startNodesInProcess = isQuasarAgentSpecified(), cordappsForAllNodes = emptyList())) {
|
||||
assertFailsWith(DatabaseIncompatibleException::class) {
|
||||
assertFailsWith(ConfigurationException::class) {
|
||||
startNode(NodeParameters(customOverrides = mapOf("database.initialiseSchema" to "false"))).getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -92,7 +92,8 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
|
||||
internalDriver(
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
notarySpecs = emptyList()
|
||||
notarySpecs = emptyList(),
|
||||
allowHibernateToManageAppSchema = false
|
||||
) {
|
||||
val alice = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow() as NodeHandleInternal
|
||||
val nextParams = networkMapServer.networkParameters.copy(
|
||||
@ -146,7 +147,8 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
|
||||
internalDriver(
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
notarySpecs = emptyList()
|
||||
notarySpecs = emptyList(),
|
||||
allowHibernateToManageAppSchema = false
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow()
|
||||
assertDownloadedNetworkParameters(aliceNode)
|
||||
@ -175,7 +177,8 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
|
||||
portAllocation = portAllocation,
|
||||
compatibilityZone = compatibilityZone,
|
||||
notarySpecs = emptyList(),
|
||||
systemProperties = mapOf("net.corda.node.internal.nodeinfo.publish.interval" to 1.seconds.toString())
|
||||
systemProperties = mapOf("net.corda.node.internal.nodeinfo.publish.interval" to 1.seconds.toString()),
|
||||
allowHibernateToManageAppSchema = false
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow()
|
||||
val aliceNodeInfo = aliceNode.nodeInfo.serialize().hash
|
||||
|
@ -57,7 +57,7 @@ class RpcExceptionHandlingTest {
|
||||
}
|
||||
}
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = listOf(enclosedCordapp()))) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = listOf(enclosedCordapp()), allowHibernateToManageAppSchema = false)) {
|
||||
val devModeNode = startNode(params, BOB_NAME).getOrThrow()
|
||||
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
||||
|
||||
@ -76,7 +76,7 @@ class RpcExceptionHandlingTest {
|
||||
rpc.startFlow(::FlowExceptionFlow, expectedMessage, expectedErrorId).returnValue.getOrThrow()
|
||||
}
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = listOf(enclosedCordapp()))) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = listOf(enclosedCordapp()), allowHibernateToManageAppSchema = false)) {
|
||||
val devModeNode = startNode(params, BOB_NAME).getOrThrow()
|
||||
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
||||
|
||||
@ -108,7 +108,7 @@ class RpcExceptionHandlingTest {
|
||||
nodeA.rpc.startFlow(::InitFlow, nodeB.nodeInfo.singleIdentity()).returnValue.getOrThrow()
|
||||
}
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = listOf(enclosedCordapp()))) {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = listOf(enclosedCordapp()), allowHibernateToManageAppSchema = false)) {
|
||||
|
||||
assertThatThrownBy { scenario(ALICE_NAME, BOB_NAME,true) }.isInstanceOfSatisfying(CordaRuntimeException::class.java) { exception ->
|
||||
|
||||
|
@ -443,11 +443,11 @@ class VaultObserverExceptionTest {
|
||||
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
), inMemoryDB = false)
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
@ -532,12 +532,12 @@ class VaultObserverExceptionTest {
|
||||
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
@ -609,12 +609,12 @@ class VaultObserverExceptionTest {
|
||||
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
@ -684,12 +684,12 @@ class VaultObserverExceptionTest {
|
||||
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
@ -741,12 +741,12 @@ class VaultObserverExceptionTest {
|
||||
fun `Accessing NodeVaultService rawUpdates from a flow is not allowed` () {
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")
|
||||
),
|
||||
inMemoryDB = false)
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
|
||||
@ -771,12 +771,12 @@ class VaultObserverExceptionTest {
|
||||
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.transactionfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")),
|
||||
inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.transactionfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")),
|
||||
inMemoryDB = false)
|
||||
) {
|
||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||
|
||||
@ -802,12 +802,12 @@ class VaultObserverExceptionTest {
|
||||
|
||||
val user = User("user", "foo", setOf(Permissions.all()))
|
||||
driver(DriverParameters(startNodesInProcess = true,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.transactionfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")),
|
||||
inMemoryDB = false)
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("com.r3.dbfailure.contracts"),
|
||||
findCordapp("com.r3.dbfailure.workflows"),
|
||||
findCordapp("com.r3.transactionfailure.workflows"),
|
||||
findCordapp("com.r3.dbfailure.schemas")),
|
||||
inMemoryDB = false)
|
||||
) {
|
||||
// Subscribing with custom SafeSubscriber; the custom SafeSubscriber will not get replaced by a ResilientSubscriber
|
||||
// meaning that it will behave as a SafeSubscriber; it will get unsubscribed upon throwing an error.
|
||||
|
@ -48,6 +48,14 @@ open class SharedNodeCmdLineOptions {
|
||||
)
|
||||
var devMode: Boolean? = null
|
||||
|
||||
@Option(
|
||||
names = ["--allow-hibernate-to-manage-app-schema"],
|
||||
description = ["Allows hibernate to create/modify app schema for CorDapps based on their mapped schema.",
|
||||
"Use this for rapid app development or for compatibility with pre-4.6 CorDapps.",
|
||||
"Only available in dev mode."]
|
||||
)
|
||||
var allowHibernateToManangeAppSchema: Boolean = false
|
||||
|
||||
open fun parseConfiguration(configuration: Config): Valid<NodeConfiguration> {
|
||||
val option = Configuration.Options(strict = unknownConfigKeysPolicy == UnknownConfigKeysPolicy.FAIL)
|
||||
return configuration.parseAsNodeConfiguration(option)
|
||||
|
@ -210,7 +210,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
||||
val busyNodeLatch: ReusableLatch = ReusableLatch(),
|
||||
djvmBootstrapSource: ApiSource = EmptyApi,
|
||||
djvmCordaSource: UserSource? = null) : SingletonSerializeAsToken() {
|
||||
djvmCordaSource: UserSource? = null,
|
||||
protected val allowHibernateToManageAppSchema: Boolean = false) : SingletonSerializeAsToken() {
|
||||
|
||||
protected abstract val log: Logger
|
||||
@Suppress("LeakingThis")
|
||||
@ -222,6 +223,11 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
|
||||
protected val runOnStop = ArrayList<() -> Any?>()
|
||||
|
||||
protected open val runMigrationScripts: Boolean = configuredDbIsInMemory()
|
||||
|
||||
// if the configured DB is in memory, we will need to run db migrations, as the db does not persist between runs.
|
||||
private fun configuredDbIsInMemory() = configuration.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:mem:")
|
||||
|
||||
init {
|
||||
(serverThread as? ExecutorService)?.let {
|
||||
runOnStop += {
|
||||
@ -233,6 +239,12 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
quasarExcludePackages(configuration)
|
||||
|
||||
if (allowHibernateToManageAppSchema && !configuration.devMode) {
|
||||
throw ConfigurationException("Hibernate can only be used to manage app schema in development while using dev mode. " +
|
||||
"Please remove the --allow-hibernate-to-manage-app-schema command line flag and provide schema migration scripts for your CorDapps."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val notaryLoader = configuration.notary?.let {
|
||||
@ -248,7 +260,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
schemaService,
|
||||
configuration.dataSourceProperties,
|
||||
cacheFactory,
|
||||
cordappLoader.appClassLoader)
|
||||
cordappLoader.appClassLoader,
|
||||
allowHibernateToManageAppSchema)
|
||||
|
||||
private val transactionSupport = CordaTransactionSupportImpl(database)
|
||||
|
||||
@ -458,6 +471,33 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
open fun runDatabaseMigrationScripts() {
|
||||
check(started == null) { "Node has already been started" }
|
||||
Node.printBasicNodeInfo("Running database schema migration scripts ...")
|
||||
val props = configuration.dataSourceProperties
|
||||
if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.")
|
||||
database.startHikariPool(props, schemaService.internalSchemas(), metricRegistry, this.cordappLoader, configuration.baseDirectory, configuration.myLegalName, runMigrationScripts = true)
|
||||
// Now log the vendor string as this will also cause a connection to be tested eagerly.
|
||||
logVendorString(database, log)
|
||||
if (allowHibernateToManageAppSchema) {
|
||||
Node.printBasicNodeInfo("Initialising CorDapps to get schemas created by hibernate")
|
||||
val trustRoot = initKeyStores()
|
||||
networkMapClient?.start(trustRoot)
|
||||
val (netParams, signedNetParams) = NetworkParametersReader(trustRoot, networkMapClient, configuration.baseDirectory).read()
|
||||
log.info("Loaded network parameters: $netParams")
|
||||
check(netParams.minimumPlatformVersion <= versionInfo.platformVersion) {
|
||||
"Node's platform version is lower than network's required minimumPlatformVersion"
|
||||
}
|
||||
networkMapCache.start(netParams.notaries)
|
||||
|
||||
database.transaction {
|
||||
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoot)
|
||||
cordappProvider.start()
|
||||
}
|
||||
}
|
||||
Node.printBasicNodeInfo("Database migration done.")
|
||||
}
|
||||
|
||||
open fun start(): S {
|
||||
check(started == null) { "Node has already been started" }
|
||||
|
||||
@ -946,7 +986,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
protected open fun startDatabase() {
|
||||
val props = configuration.dataSourceProperties
|
||||
if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.")
|
||||
database.startHikariPool(props, configuration.database, schemaService.internalSchemas(), metricRegistry, this.cordappLoader, configuration.baseDirectory, configuration.myLegalName)
|
||||
database.startHikariPool(props, schemaService.internalSchemas(), metricRegistry, this.cordappLoader, configuration.baseDirectory, configuration.myLegalName, runMigrationScripts = runMigrationScripts)
|
||||
// Now log the vendor string as this will also cause a connection to be tested eagerly.
|
||||
logVendorString(database, log)
|
||||
}
|
||||
@ -1313,13 +1353,15 @@ class FlowStarterImpl(private val smm: StateMachineManager, private val flowLogi
|
||||
|
||||
class ConfigurationException(message: String) : CordaException(message)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun createCordaPersistence(databaseConfig: DatabaseConfig,
|
||||
wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
|
||||
wellKnownPartyFromAnonymous: (AbstractParty) -> Party?,
|
||||
schemaService: SchemaService,
|
||||
hikariProperties: Properties,
|
||||
cacheFactory: NamedCacheFactory,
|
||||
customClassLoader: ClassLoader?): CordaPersistence {
|
||||
customClassLoader: ClassLoader?,
|
||||
allowHibernateToManageAppSchema: Boolean = false): CordaPersistence {
|
||||
// Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately
|
||||
// Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default
|
||||
// so we end up providing both descriptor and converter. We should re-examine this in later versions to see if
|
||||
@ -1330,25 +1372,38 @@ fun createCordaPersistence(databaseConfig: DatabaseConfig,
|
||||
|
||||
val jdbcUrl = hikariProperties.getProperty("dataSource.url", "")
|
||||
return CordaPersistence(
|
||||
databaseConfig,
|
||||
schemaService.schemas,
|
||||
jdbcUrl,
|
||||
cacheFactory,
|
||||
attributeConverters, customClassLoader,
|
||||
errorHandler = { e ->
|
||||
// "corrupting" a DatabaseTransaction only inside a flow state machine execution
|
||||
FlowStateMachineImpl.currentStateMachine()?.let {
|
||||
// register only the very first exception thrown throughout a chain of logical transactions
|
||||
setException(e)
|
||||
}
|
||||
})
|
||||
databaseConfig,
|
||||
schemaService.schemas,
|
||||
jdbcUrl,
|
||||
cacheFactory,
|
||||
attributeConverters, customClassLoader,
|
||||
errorHandler = { e ->
|
||||
// "corrupting" a DatabaseTransaction only inside a flow state machine execution
|
||||
FlowStateMachineImpl.currentStateMachine()?.let {
|
||||
// register only the very first exception thrown throughout a chain of logical transactions
|
||||
setException(e)
|
||||
}
|
||||
},
|
||||
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema)
|
||||
}
|
||||
|
||||
fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name) {
|
||||
@Suppress("LongParameterList", "ComplexMethod", "ThrowsCount")
|
||||
fun CordaPersistence.startHikariPool(
|
||||
hikariProperties: Properties,
|
||||
schemas: Set<MappedSchema>,
|
||||
metricRegistry: MetricRegistry? = null,
|
||||
cordappLoader: CordappLoader? = null,
|
||||
currentDir: Path? = null,
|
||||
ourName: CordaX500Name,
|
||||
runMigrationScripts: Boolean = false) {
|
||||
try {
|
||||
val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry)
|
||||
val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, cordappLoader, currentDir, ourName)
|
||||
schemaMigration.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
val schemaMigration = SchemaMigration(schemas, dataSource, cordappLoader, currentDir, ourName)
|
||||
if (runMigrationScripts) {
|
||||
schemaMigration.runMigration(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||
} else {
|
||||
schemaMigration.checkState()
|
||||
}
|
||||
start(dataSource)
|
||||
} catch (ex: Exception) {
|
||||
when {
|
||||
|
@ -125,7 +125,8 @@ open class Node(configuration: NodeConfiguration,
|
||||
flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides),
|
||||
cacheFactoryPrototype: BindableNamedCacheFactory = DefaultNamedCacheFactory(),
|
||||
djvmBootstrapSource: ApiSource = createBootstrapSource(configuration),
|
||||
djvmCordaSource: UserSource? = createCordaSource(configuration)
|
||||
djvmCordaSource: UserSource? = createCordaSource(configuration),
|
||||
allowHibernateToManageAppSchema: Boolean = false
|
||||
) : AbstractNode<NodeInfo>(
|
||||
configuration,
|
||||
createClock(configuration),
|
||||
@ -135,7 +136,8 @@ open class Node(configuration: NodeConfiguration,
|
||||
// Under normal (non-test execution) it will always be "1"
|
||||
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1),
|
||||
djvmBootstrapSource = djvmBootstrapSource,
|
||||
djvmCordaSource = djvmCordaSource
|
||||
djvmCordaSource = djvmCordaSource,
|
||||
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema
|
||||
) {
|
||||
|
||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): NodeInfo =
|
||||
@ -559,6 +561,13 @@ open class Node(configuration: NodeConfiguration,
|
||||
return super.generateAndSaveNodeInfo()
|
||||
}
|
||||
|
||||
override fun runDatabaseMigrationScripts() {
|
||||
if (allowHibernateToManageAppSchema) {
|
||||
initialiseSerialization()
|
||||
}
|
||||
super.runDatabaseMigrationScripts()
|
||||
}
|
||||
|
||||
override fun start(): NodeInfo {
|
||||
registerDefaultExceptionHandler()
|
||||
initialiseSerialization()
|
||||
|
@ -76,10 +76,16 @@ open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") {
|
||||
private val justGenerateRpcSslCertsCli by lazy { GenerateRpcSslCertsCli(startup) }
|
||||
private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) }
|
||||
private val validateConfigurationCli by lazy { ValidateConfigurationCli() }
|
||||
private val runMigrationScriptsCli by lazy { RunMigrationScriptsCli(startup) }
|
||||
|
||||
override fun initLogging(): Boolean = this.initLogging(cmdLineOptions.baseDirectory)
|
||||
|
||||
override fun additionalSubCommands() = setOf(networkCacheCli, justGenerateNodeInfoCli, justGenerateRpcSslCertsCli, initialRegistrationCli, validateConfigurationCli)
|
||||
override fun additionalSubCommands() = setOf(networkCacheCli,
|
||||
justGenerateNodeInfoCli,
|
||||
justGenerateRpcSslCertsCli,
|
||||
initialRegistrationCli,
|
||||
validateConfigurationCli,
|
||||
runMigrationScriptsCli)
|
||||
|
||||
override fun call(): Int {
|
||||
if (!validateBaseDirectory()) {
|
||||
@ -201,7 +207,7 @@ open class NodeStartup : NodeStartupLogging {
|
||||
|
||||
protected open fun preNetworkRegistration(conf: NodeConfiguration) = Unit
|
||||
|
||||
open fun createNode(conf: NodeConfiguration, versionInfo: VersionInfo): Node = Node(conf, versionInfo)
|
||||
open fun createNode(conf: NodeConfiguration, versionInfo: VersionInfo): Node = Node(conf, versionInfo, allowHibernateToManageAppSchema = cmdLineOptions.allowHibernateToManangeAppSchema)
|
||||
|
||||
fun startNode(node: Node, startTime: Long) {
|
||||
if (node.configuration.devMode) {
|
||||
|
@ -0,0 +1,16 @@
|
||||
package net.corda.node.internal.subcommands
|
||||
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeCliCommand
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.node.internal.RunAfterNodeInitialisation
|
||||
|
||||
class RunMigrationScriptsCli(startup: NodeStartup) : NodeCliCommand("run-migration-scripts", "Run the database migration scripts and create or update schemas", startup) {
|
||||
override fun runProgram(): Int {
|
||||
return startup.initialiseAndRun(cmdLineOptions, object : RunAfterNodeInitialisation {
|
||||
override fun run(node: Node) {
|
||||
node.runDatabaseMigrationScripts()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.config.SslConfiguration
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.SchemaInitializationType
|
||||
import net.corda.tools.shell.SSHDConfiguration
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
@ -129,8 +128,6 @@ data class NodeConfigurationImpl(
|
||||
fun messagingServerExternal(messagingServerAddress: NetworkHostAndPort?) = messagingServerAddress != null
|
||||
|
||||
fun database(devMode: Boolean) = DatabaseConfig(
|
||||
initialiseSchema = devMode,
|
||||
initialiseAppSchema = if(devMode) SchemaInitializationType.UPDATE else SchemaInitializationType.VALIDATE,
|
||||
exportHibernateJMXStatistics = devMode
|
||||
)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import net.corda.common.validation.internal.Validated.Companion.invalid
|
||||
import net.corda.common.validation.internal.Validated.Companion.valid
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.node.internal.ConfigurationException
|
||||
import net.corda.node.services.config.AuthDataSourceType
|
||||
import net.corda.node.services.config.CertChainPolicyConfig
|
||||
import net.corda.node.services.config.CertChainPolicyType
|
||||
@ -44,7 +45,6 @@ import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||
import net.corda.nodeapi.internal.persistence.SchemaInitializationType
|
||||
import net.corda.notary.experimental.bftsmart.BFTSmartConfig
|
||||
import net.corda.notary.experimental.raft.RaftConfig
|
||||
import net.corda.tools.shell.SSHDConfiguration
|
||||
@ -267,16 +267,32 @@ internal object SSHDConfigurationSpec : Configuration.Specification<SSHDConfigur
|
||||
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<SSHDConfiguration> = attempt<SSHDConfiguration, IllegalArgumentException> { SSHDConfiguration(configuration.withOptions(options)[port]) }
|
||||
}
|
||||
|
||||
enum class SchemaInitializationType{
|
||||
NONE,
|
||||
VALIDATE,
|
||||
UPDATE
|
||||
}
|
||||
|
||||
internal object DatabaseConfigSpec : Configuration.Specification<DatabaseConfig>("DatabaseConfig") {
|
||||
private val initialiseSchema by boolean().optional().withDefaultValue(DatabaseConfig.Defaults.initialiseSchema)
|
||||
private val initialiseAppSchema by enum(SchemaInitializationType::class).optional().withDefaultValue(DatabaseConfig.Defaults.initialiseAppSchema)
|
||||
private val transactionIsolationLevel by enum(TransactionIsolationLevel::class).optional().withDefaultValue(DatabaseConfig.Defaults.transactionIsolationLevel)
|
||||
private val initialiseSchema by boolean().optional()
|
||||
private val initialiseAppSchema by enum(SchemaInitializationType::class).optional()
|
||||
private val transactionIsolationLevel by enum(TransactionIsolationLevel::class).optional()
|
||||
private val exportHibernateJMXStatistics by boolean().optional().withDefaultValue(DatabaseConfig.Defaults.exportHibernateJMXStatistics)
|
||||
private val mappedSchemaCacheSize by long().optional().withDefaultValue(DatabaseConfig.Defaults.mappedSchemaCacheSize)
|
||||
|
||||
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<DatabaseConfig> {
|
||||
if (initialiseSchema.isSpecifiedBy(configuration)){
|
||||
throw ConfigurationException("Unsupported configuration database/initialiseSchema - this option has been removed, please use the run-migration-scripts sub-command or the database management tool to modify schemas")
|
||||
}
|
||||
if (initialiseAppSchema.isSpecifiedBy(configuration)){
|
||||
throw ConfigurationException("Unsupported configuration database/initialiseAppSchema - this option has been removed, please use the run-migration-scripts sub-command or the database management tool to modify schemas")
|
||||
}
|
||||
if (transactionIsolationLevel.isSpecifiedBy(configuration)){
|
||||
throw ConfigurationException("Unsupported configuration database/transactionIsolationLevel - this option has been removed and cannot be changed")
|
||||
}
|
||||
val config = configuration.withOptions(options)
|
||||
return valid(DatabaseConfig(config[initialiseSchema], config[initialiseAppSchema], config[transactionIsolationLevel], config[exportHibernateJMXStatistics], config[mappedSchemaCacheSize]))
|
||||
|
||||
return valid(DatabaseConfig(config[exportHibernateJMXStatistics], config[mappedSchemaCacheSize]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
additionalP2PAddresses = []
|
||||
crlCheckSoftFail = true
|
||||
database = {
|
||||
transactionIsolationLevel = "REPEATABLE_READ"
|
||||
exportHibernateJMXStatistics = "false"
|
||||
}
|
||||
dataSourceProperties = {
|
||||
|
@ -7,7 +7,6 @@ import net.corda.node.internal.startHikariPool
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
@ -91,10 +90,10 @@ class DbMapDeadlockTest {
|
||||
|
||||
fun recreateDeadlock(hikariProperties: Properties) {
|
||||
val cacheFactory = TestingNamedCacheFactory()
|
||||
val dbConfig = DatabaseConfig(initialiseSchema = true, transactionIsolationLevel = TransactionIsolationLevel.READ_COMMITTED)
|
||||
val dbConfig = DatabaseConfig()
|
||||
val schemaService = NodeSchemaService(extraSchemas = setOf(LockDbSchemaV2))
|
||||
createCordaPersistence(dbConfig, { null }, { null }, schemaService, hikariProperties, cacheFactory, null).apply {
|
||||
startHikariPool(hikariProperties, dbConfig, schemaService.schemas, ourName = TestIdentity(ALICE_NAME, 70).name)
|
||||
startHikariPool(hikariProperties, schemaService.schemas, ourName = TestIdentity(ALICE_NAME, 70).name, runMigrationScripts = true)
|
||||
}.use { persistence ->
|
||||
|
||||
// First clean up any remains from previous test runs
|
||||
|
@ -48,6 +48,7 @@ import net.corda.testing.internal.vault.VaultFiller
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.`in`
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.hibernate.SessionFactory
|
||||
@ -976,7 +977,7 @@ class HibernateConfigurationTest {
|
||||
doReturn(it.party).whenever(mock).wellKnownPartyFromX500Name(it.name)
|
||||
}
|
||||
}
|
||||
database = configureDatabase(dataSourceProps, DatabaseConfig(initialiseSchema = initialiseSchema), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService)
|
||||
database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService, runMigrationScripts = initialiseSchema, allowHibernateToManageAppSchema = initialiseSchema)
|
||||
return database
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ dataSourceProperties = {
|
||||
dataSource.password = ""
|
||||
}
|
||||
database = {
|
||||
transactionIsolationLevel = "REPEATABLE_READ"
|
||||
exportHibernateJMXStatistics = "false"
|
||||
}
|
||||
p2pAddress = "localhost:2233"
|
||||
|
@ -12,7 +12,6 @@ dataSourceProperties = {
|
||||
dataSource.password = ""
|
||||
}
|
||||
database = {
|
||||
transactionIsolationLevel = "REPEATABLE_READ"
|
||||
exportHibernateJMXStatistics = "false"
|
||||
}
|
||||
p2pAddress = "localhost:2233"
|
||||
|
@ -202,7 +202,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||
djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
|
||||
djvmCordaSource = defaultParameters.djvmCordaSource,
|
||||
environmentVariables = defaultParameters.environmentVariables
|
||||
environmentVariables = defaultParameters.environmentVariables,
|
||||
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl
|
||||
@ -263,7 +264,8 @@ data class DriverParameters(
|
||||
val cordappsForAllNodes: Collection<TestCordapp>? = null,
|
||||
val djvmBootstrapSource: Path? = null,
|
||||
val djvmCordaSource: List<Path> = emptyList(),
|
||||
val environmentVariables : Map<String, String> = emptyMap()
|
||||
val environmentVariables : Map<String, String> = emptyMap(),
|
||||
val allowHibernateToManageAppSchema: Boolean = true
|
||||
) {
|
||||
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes)
|
||||
|
||||
@ -424,6 +426,7 @@ data class DriverParameters(
|
||||
fun withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource)
|
||||
fun withDjvmCordaSource(djvmCordaSource: List<Path>): DriverParameters = copy(djvmCordaSource = djvmCordaSource)
|
||||
fun withEnvironmentVariables(variables : Map<String, String>): DriverParameters = copy(environmentVariables = variables)
|
||||
fun withAllowHibernateToManageAppSchema(value: Boolean): DriverParameters = copy(allowHibernateToManageAppSchema = value)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
|
@ -1,4 +1,4 @@
|
||||
@file:Suppress("TooManyFunctions")
|
||||
@file:Suppress("TooManyFunctions", "Deprecation")
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import co.paralleluniverse.fibers.instrument.JavaAgent
|
||||
@ -15,6 +15,7 @@ import net.corda.core.concurrent.firstOf
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.concurrent.doOnComplete
|
||||
import net.corda.core.internal.concurrent.doOnError
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
@ -22,12 +23,12 @@ import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_LICENCE
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VENDOR
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_NAME
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_LICENCE
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_NAME
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VENDOR
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.MIN_PLATFORM_VERSION
|
||||
@ -51,6 +52,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.node.NodeRegistrationOption
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.Node
|
||||
@ -82,11 +84,17 @@ import net.corda.notary.experimental.raft.RaftConfig
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.driver.*
|
||||
import net.corda.testing.driver.DriverDSL
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.NodeParameters
|
||||
import net.corda.testing.driver.NotaryHandle
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.WebserverHandle
|
||||
import net.corda.testing.driver.internal.InProcessImpl
|
||||
import net.corda.testing.driver.internal.NodeHandleInternal
|
||||
import net.corda.testing.driver.internal.OutOfProcessImpl
|
||||
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import okhttp3.OkHttpClient
|
||||
@ -139,7 +147,8 @@ class DriverDSLImpl(
|
||||
val cordappsForAllNodes: Collection<TestCordappInternal>?,
|
||||
val djvmBootstrapSource: Path?,
|
||||
val djvmCordaSource: List<Path>,
|
||||
val environmentVariables : Map<String, String>
|
||||
val environmentVariables : Map<String, String>,
|
||||
val allowHibernateToManageAppSchema: Boolean = true
|
||||
) : InternalDriverDSL {
|
||||
|
||||
private var _executorService: ScheduledExecutorService? = null
|
||||
@ -248,30 +257,40 @@ class DriverDSLImpl(
|
||||
// TODO: Derive name from the full picked name, don't just wrap the common name
|
||||
val name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB")
|
||||
|
||||
val config = createConfig(name, parameters, p2pAddress)
|
||||
val registrationFuture = if (compatibilityZone?.rootCert != null) {
|
||||
// We don't need the network map to be available to be able to register the node
|
||||
startNodeRegistration(name, compatibilityZone.rootCert, compatibilityZone.config(), parameters.customOverrides)
|
||||
createSchema(config, false).doOnComplete { startNodeRegistration(it, compatibilityZone.rootCert, compatibilityZone.config()) }
|
||||
} else {
|
||||
doneFuture(Unit)
|
||||
doneFuture(config)
|
||||
}
|
||||
|
||||
return registrationFuture.flatMap {
|
||||
networkMapAvailability.flatMap {
|
||||
return registrationFuture.flatMap { conf ->
|
||||
networkMapAvailability.flatMap {networkMap ->
|
||||
// But starting the node proper does require the network map
|
||||
startRegisteredNode(name, it, parameters, p2pAddress, bytemanPort)
|
||||
startRegisteredNode(conf, networkMap, parameters, bytemanPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun startRegisteredNode(name: CordaX500Name,
|
||||
private fun startRegisteredNode(config: NodeConfig,
|
||||
localNetworkMap: LocalNetworkMap?,
|
||||
parameters: NodeParameters,
|
||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
|
||||
bytemanPort: Int? = null): CordaFuture<NodeHandle> {
|
||||
val webAddress = portAllocation.nextHostAndPort()
|
||||
return startNodeInternal(config, webAddress, localNetworkMap, parameters, bytemanPort)
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun createConfig(
|
||||
providedName: CordaX500Name,
|
||||
parameters: NodeParameters,
|
||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort()
|
||||
): NodeConfig {
|
||||
val baseDirectory = baseDirectory(providedName).createDirectories()
|
||||
val rpcAddress = portAllocation.nextHostAndPort()
|
||||
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
||||
val webAddress = portAllocation.nextHostAndPort()
|
||||
val users = parameters.rpcUsers.map { it.copy(permissions = it.permissions + DRIVER_REQUIRED_PERMISSIONS) }
|
||||
val czUrlConfig = when (compatibilityZone) {
|
||||
null -> emptyMap()
|
||||
@ -292,50 +311,41 @@ class DriverDSLImpl(
|
||||
val flowOverrideConfig = FlowOverrideConfig(parameters.flowOverrides.map { FlowOverride(it.key.canonicalName, it.value.canonicalName) })
|
||||
|
||||
val overrides = configOf(
|
||||
NodeConfiguration::myLegalName.name to name.toString(),
|
||||
NodeConfiguration::myLegalName.name to providedName.toString(),
|
||||
NodeConfiguration::p2pAddress.name to p2pAddress.toString(),
|
||||
"rpcSettings.address" to rpcAddress.toString(),
|
||||
"rpcSettings.adminAddress" to rpcAdminAddress.toString(),
|
||||
NodeConfiguration::useTestClock.name to useTestClock,
|
||||
NodeConfiguration::rpcUsers.name to if (users.isEmpty()) defaultRpcUserList else users.map { it.toConfig().root().unwrapped() },
|
||||
NodeConfiguration::rpcUsers.name to if (users.isEmpty()) defaultRpcUserList else users.map {
|
||||
it.toConfig().root().unwrapped()
|
||||
},
|
||||
NodeConfiguration::verifierType.name to parameters.verifierType.name,
|
||||
NodeConfiguration::flowOverrides.name to flowOverrideConfig.toConfig().root().unwrapped(),
|
||||
NodeConfiguration::additionalNodeInfoPollingFrequencyMsec.name to 1000
|
||||
) + czUrlConfig + jmxConfig + parameters.customOverrides
|
||||
val config = NodeConfig(
|
||||
return NodeConfig(
|
||||
ConfigHelper.loadConfig(
|
||||
baseDirectory = baseDirectory(name),
|
||||
baseDirectory = baseDirectory,
|
||||
allowMissingConfig = true,
|
||||
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
|
||||
).withDJVMConfig(djvmBootstrapSource, djvmCordaSource)
|
||||
).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, localNetworkMap, parameters, bytemanPort)
|
||||
}
|
||||
|
||||
private fun createSchema(config: NodeConfig, hibernateForAppSchema: Boolean): CordaFuture<NodeConfig> {
|
||||
if (startNodesInProcess || inMemoryDB) return doneFuture(config)
|
||||
return startOutOfProcessMiniNode(config,
|
||||
listOfNotNull(
|
||||
"run-migration-scripts",
|
||||
if (hibernateForAppSchema) "--allow-hibernate-to-manage-app-schema" else null
|
||||
).toTypedArray()).map { config }
|
||||
}
|
||||
|
||||
private fun startNodeRegistration(
|
||||
providedName: CordaX500Name,
|
||||
config: NodeConfig,
|
||||
rootCert: X509Certificate,
|
||||
networkServicesConfig: NetworkServicesConfig,
|
||||
customOverrides: Map<String, Any?> = mapOf()
|
||||
networkServicesConfig: NetworkServicesConfig
|
||||
): CordaFuture<NodeConfig> {
|
||||
val baseDirectory = baseDirectory(providedName).createDirectories()
|
||||
val overrides = configOf(
|
||||
"p2pAddress" to portAllocation.nextHostAndPort().toString(),
|
||||
"compatibilityZoneURL" to networkServicesConfig.doormanURL.toString(),
|
||||
"myLegalName" to providedName.toString(),
|
||||
"rpcSettings" to mapOf(
|
||||
"address" to portAllocation.nextHostAndPort().toString(),
|
||||
"adminAddress" to portAllocation.nextHostAndPort().toString()
|
||||
),
|
||||
"additionalNodeInfoPollingFrequencyMsec" to 1000,
|
||||
"devMode" to false) + customOverrides
|
||||
val config = NodeConfig(
|
||||
ConfigHelper.loadConfig(
|
||||
baseDirectory = baseDirectory,
|
||||
allowMissingConfig = true,
|
||||
configOverrides = overrides
|
||||
).withDJVMConfig(djvmBootstrapSource, djvmCordaSource)
|
||||
).checkAndOverrideForInMemoryDB()
|
||||
|
||||
val versionInfo = VersionInfo(PLATFORM_VERSION, "1", "1", "1")
|
||||
config.corda.certificatesDirectory.createDirectories()
|
||||
@ -410,7 +420,7 @@ class DriverDSLImpl(
|
||||
val notaryInfosFuture = if (compatibilityZone == null) {
|
||||
// If no CZ is specified then the driver does the generation of the network parameters and the copying of the
|
||||
// node info files.
|
||||
startNotaryIdentityGeneration().map { notaryInfos -> Pair(notaryInfos, LocalNetworkMap(notaryInfos)) }
|
||||
startNotaryIdentityGeneration().map { notaryInfos -> Pair(notaryInfos, LocalNetworkMap(notaryInfos.map{it.second})) }
|
||||
} else {
|
||||
// Otherwise it's the CZ's job to distribute thse via the HTTP network map, as that is what the nodes will be expecting.
|
||||
val notaryInfosFuture = if (compatibilityZone.rootCert == null) {
|
||||
@ -421,7 +431,7 @@ class DriverDSLImpl(
|
||||
startAllNotaryRegistrations(compatibilityZone.rootCert, compatibilityZone)
|
||||
}
|
||||
notaryInfosFuture.map { notaryInfos ->
|
||||
compatibilityZone.publishNotaries(notaryInfos)
|
||||
compatibilityZone.publishNotaries(notaryInfos.map{it.second})
|
||||
Pair(notaryInfos, null)
|
||||
}
|
||||
}
|
||||
@ -429,9 +439,9 @@ class DriverDSLImpl(
|
||||
networkMapAvailability = notaryInfosFuture.map { it.second }
|
||||
|
||||
_notaries = notaryInfosFuture.map { (notaryInfos, localNetworkMap) ->
|
||||
val listOfFutureNodeHandles = startNotaries(localNetworkMap, notaryCustomOverrides)
|
||||
notaryInfos.zip(listOfFutureNodeHandles) { (identity, validating), nodeHandlesFuture ->
|
||||
NotaryHandle(identity, validating, nodeHandlesFuture)
|
||||
val listOfFutureNodeHandles = startNotaries(notaryInfos.map{it.first}, localNetworkMap, notaryCustomOverrides)
|
||||
notaryInfos.zip(listOfFutureNodeHandles) { (_, notaryInfo), nodeHandlesFuture ->
|
||||
NotaryHandle(notaryInfo.identity, notaryInfo.validating, nodeHandlesFuture)
|
||||
}
|
||||
}
|
||||
try {
|
||||
@ -471,9 +481,12 @@ class DriverDSLImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun startNotaryIdentityGeneration(): CordaFuture<List<NotaryInfo>> {
|
||||
private fun startNotaryIdentityGeneration(): CordaFuture<List<Pair<NodeConfig,NotaryInfo>>> {
|
||||
return executorService.fork {
|
||||
notarySpecs.map { spec ->
|
||||
val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating))
|
||||
val parameters = NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig + notaryCustomOverrides, maximumHeapSize = spec.maximumHeapSize)
|
||||
val config = createConfig(spec.name, parameters)
|
||||
val identity = when (spec.cluster) {
|
||||
null -> {
|
||||
DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(spec.name), spec.name)
|
||||
@ -499,14 +512,14 @@ class DriverDSLImpl(
|
||||
}
|
||||
else -> throw UnsupportedOperationException("Cluster spec ${spec.cluster} not supported by Driver")
|
||||
}
|
||||
NotaryInfo(identity, spec.validating)
|
||||
Pair(config, NotaryInfo(identity, spec.validating))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAllNotaryRegistrations(
|
||||
rootCert: X509Certificate,
|
||||
compatibilityZone: CompatibilityZoneParams): CordaFuture<List<NotaryInfo>> {
|
||||
compatibilityZone: CompatibilityZoneParams): CordaFuture<List<Pair<NodeConfig, NotaryInfo>>> {
|
||||
// Start the registration process for all the notaries together then wait for their responses.
|
||||
return notarySpecs.map { spec ->
|
||||
require(spec.cluster == null) { "Registering distributed notaries not supported" }
|
||||
@ -518,51 +531,56 @@ class DriverDSLImpl(
|
||||
spec: NotarySpec,
|
||||
rootCert: X509Certificate,
|
||||
compatibilityZone: CompatibilityZoneParams
|
||||
): CordaFuture<NotaryInfo> {
|
||||
return startNodeRegistration(spec.name, rootCert, compatibilityZone.config()).flatMap { config ->
|
||||
// Node registration only gives us the node CA cert, not the identity cert. That is only created on first
|
||||
// startup or when the node is told to just generate its node info file. We do that here.
|
||||
if (startNodesInProcess) {
|
||||
executorService.fork {
|
||||
val nodeInfo = Node(config.corda, MOCK_VERSION_INFO, initialiseSerialization = false).generateAndSaveNodeInfo()
|
||||
NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)
|
||||
}
|
||||
} else {
|
||||
// TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper
|
||||
// This causes two node info files to be generated.
|
||||
startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map {
|
||||
// Once done we have to read the signed node info file that's been generated
|
||||
val nodeInfoFile = config.corda.baseDirectory.list { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
||||
): CordaFuture<Pair<NodeConfig,NotaryInfo>> {
|
||||
val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating))
|
||||
val parameters = NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig + notaryCustomOverrides, maximumHeapSize = spec.maximumHeapSize)
|
||||
return createSchema(createConfig(spec.name, parameters), false).doOnComplete { config ->
|
||||
startNodeRegistration(config, rootCert, compatibilityZone.config())}.flatMap { config ->
|
||||
// Node registration only gives us the node CA cert, not the identity cert. That is only created on first
|
||||
// startup or when the node is told to just generate its node info file. We do that here.
|
||||
if (startNodesInProcess) {
|
||||
executorService.fork {
|
||||
val nodeInfo = Node(config.corda, MOCK_VERSION_INFO, initialiseSerialization = false).generateAndSaveNodeInfo()
|
||||
Pair(config, NotaryInfo(nodeInfo.legalIdentities[0], spec.validating))
|
||||
}
|
||||
} else {
|
||||
// TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper
|
||||
// This causes two node info files to be generated.
|
||||
startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map {
|
||||
// Once done we have to read the signed node info file that's been generated
|
||||
val nodeInfoFile = config.corda.baseDirectory.list { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst()
|
||||
.get()
|
||||
}
|
||||
val nodeInfo = nodeInfoFile.readObject<SignedNodeInfo>().verified()
|
||||
Pair(config,NotaryInfo(nodeInfo.legalIdentities[0], spec.validating))
|
||||
}
|
||||
val nodeInfo = nodeInfoFile.readObject<SignedNodeInfo>().verified()
|
||||
NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun generateNodeNames(spec: NotarySpec): List<CordaX500Name> {
|
||||
return (0 until spec.cluster!!.clusterSize).map { spec.name.copy(organisation = "${spec.name.organisation}-$it") }
|
||||
}
|
||||
|
||||
private fun startNotaries(localNetworkMap: LocalNetworkMap?, customOverrides: Map<String, Any?>): List<CordaFuture<List<NodeHandle>>> {
|
||||
return notarySpecs.map {
|
||||
when (it.cluster) {
|
||||
null -> startSingleNotary(it, localNetworkMap, customOverrides)
|
||||
private fun startNotaries(configs: List<NodeConfig>, localNetworkMap: LocalNetworkMap?, customOverrides: Map<String, Any?>): List<CordaFuture<List<NodeHandle>>> {
|
||||
return notarySpecs.zip(configs).map { (spec, config) ->
|
||||
when (spec.cluster) {
|
||||
null -> startSingleNotary(config, spec, localNetworkMap, customOverrides)
|
||||
is ClusterSpec.Raft,
|
||||
// DummyCluster is used for testing the notary communication path, and it does not matter
|
||||
// which underlying consensus algorithm is used, so we just stick to Raft
|
||||
is DummyClusterSpec -> startRaftNotaryCluster(it, localNetworkMap)
|
||||
is DummyClusterSpec -> startRaftNotaryCluster(spec, localNetworkMap)
|
||||
else -> throw IllegalArgumentException("BFT-SMaRt not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startSingleNotary(spec: NotarySpec, localNetworkMap: LocalNetworkMap?, customOverrides: Map<String, Any?>): CordaFuture<List<NodeHandle>> {
|
||||
private fun startSingleNotary(config: NodeConfig, spec: NotarySpec, localNetworkMap: LocalNetworkMap?, customOverrides: Map<String, Any?>): CordaFuture<List<NodeHandle>> {
|
||||
val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating))
|
||||
return startRegisteredNode(
|
||||
spec.name,
|
||||
config,
|
||||
localNetworkMap,
|
||||
NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig + customOverrides, maximumHeapSize = spec.maximumHeapSize)
|
||||
).map { listOf(it) }
|
||||
@ -585,20 +603,26 @@ class DriverDSLImpl(
|
||||
val nodeNames = generateNodeNames(spec)
|
||||
val clusterAddress = portAllocation.nextHostAndPort()
|
||||
|
||||
val firstParams = NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig(clusterAddress))
|
||||
val firstConfig = createSchema(createConfig(nodeNames[0], firstParams), allowHibernateToManageAppSchema)
|
||||
|
||||
// Start the first node that will bootstrap the cluster
|
||||
val firstNodeFuture = startRegisteredNode(
|
||||
nodeNames[0],
|
||||
firstConfig.getOrThrow(),
|
||||
localNetworkMap,
|
||||
NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig(clusterAddress))
|
||||
firstParams
|
||||
)
|
||||
|
||||
// All other nodes will join the cluster
|
||||
val restNodeFutures = nodeNames.drop(1).map {
|
||||
val nodeAddress = portAllocation.nextHostAndPort()
|
||||
val params = NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig(nodeAddress, clusterAddress))
|
||||
val config = createSchema(createConfig(it, params), allowHibernateToManageAppSchema)
|
||||
startRegisteredNode(
|
||||
it,
|
||||
config.getOrThrow(),
|
||||
localNetworkMap,
|
||||
NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig(nodeAddress, clusterAddress))
|
||||
params
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@ -663,7 +687,7 @@ class DriverDSLImpl(
|
||||
)
|
||||
|
||||
val nodeFuture = if (parameters.startInSameProcess ?: startNodesInProcess) {
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config)
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config, allowHibernateToManageAppSchema)
|
||||
shutdownManager.registerShutdown(
|
||||
nodeAndThreadFuture.map { (node, thread) ->
|
||||
{
|
||||
@ -689,6 +713,9 @@ class DriverDSLImpl(
|
||||
nodeFuture
|
||||
} else {
|
||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||
log.info("StartNodeInternal for ${config.corda.myLegalName.organisation} - calling create schema")
|
||||
createSchema(config, allowHibernateToManageAppSchema).getOrThrow()
|
||||
log.info("StartNodeInternal for ${config.corda.myLegalName.organisation} - create schema done")
|
||||
val process = startOutOfProcessNode(
|
||||
config,
|
||||
quasarJarPath,
|
||||
@ -699,7 +726,10 @@ class DriverDSLImpl(
|
||||
parameters.maximumHeapSize,
|
||||
parameters.logLevelOverride,
|
||||
identifier,
|
||||
environmentVariables
|
||||
environmentVariables,
|
||||
extraCmdLineFlag = listOfNotNull(
|
||||
if (allowHibernateToManageAppSchema) "--allow-hibernate-to-manage-app-schema" else null
|
||||
).toTypedArray()
|
||||
)
|
||||
|
||||
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
|
||||
@ -853,7 +883,8 @@ class DriverDSLImpl(
|
||||
|
||||
private fun startInProcessNode(
|
||||
executorService: ScheduledExecutorService,
|
||||
config: NodeConfig
|
||||
config: NodeConfig,
|
||||
allowHibernateToManageAppSchema: Boolean
|
||||
): CordaFuture<Pair<NodeWithInfo, Thread>> {
|
||||
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
|
||||
return executorService.fork {
|
||||
@ -864,7 +895,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)
|
||||
val node = InProcessNode(config.corda, MOCK_VERSION_INFO, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema)
|
||||
val nodeInfo = node.start()
|
||||
val nodeWithInfo = NodeWithInfo(node, nodeInfo)
|
||||
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
||||
@ -1241,7 +1272,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||
djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
|
||||
djvmCordaSource = defaultParameters.djvmCordaSource,
|
||||
environmentVariables = defaultParameters.environmentVariables
|
||||
environmentVariables = defaultParameters.environmentVariables,
|
||||
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
@ -1339,29 +1371,31 @@ fun <A> internalDriver(
|
||||
djvmBootstrapSource: Path? = null,
|
||||
djvmCordaSource: List<Path> = emptyList(),
|
||||
environmentVariables: Map<String, String> = emptyMap(),
|
||||
allowHibernateToManageAppSchema: Boolean = true,
|
||||
dsl: DriverDSLImpl.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
driverDsl = DriverDSLImpl(
|
||||
portAllocation = portAllocation,
|
||||
debugPortAllocation = debugPortAllocation,
|
||||
systemProperties = systemProperties,
|
||||
driverDirectory = driverDirectory.toAbsolutePath(),
|
||||
useTestClock = useTestClock,
|
||||
isDebug = isDebug,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = compatibilityZone,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes,
|
||||
djvmBootstrapSource = djvmBootstrapSource,
|
||||
djvmCordaSource = djvmCordaSource,
|
||||
environmentVariables = environmentVariables
|
||||
portAllocation = portAllocation,
|
||||
debugPortAllocation = debugPortAllocation,
|
||||
systemProperties = systemProperties,
|
||||
driverDirectory = driverDirectory.toAbsolutePath(),
|
||||
useTestClock = useTestClock,
|
||||
isDebug = isDebug,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = compatibilityZone,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes,
|
||||
djvmBootstrapSource = djvmBootstrapSource,
|
||||
djvmCordaSource = djvmCordaSource,
|
||||
environmentVariables = environmentVariables,
|
||||
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl
|
||||
|
@ -286,7 +286,8 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
args.version,
|
||||
mockFlowManager,
|
||||
args.network.getServerThread(args.id),
|
||||
args.network.busyLatch
|
||||
args.network.busyLatch,
|
||||
allowHibernateToManageAppSchema = true
|
||||
) {
|
||||
companion object {
|
||||
private val staticLog = contextLogger()
|
||||
@ -317,6 +318,8 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
}
|
||||
}
|
||||
|
||||
override val runMigrationScripts: Boolean = true
|
||||
|
||||
val mockNet = args.network
|
||||
val id = args.id
|
||||
|
||||
|
@ -152,7 +152,12 @@ constructor(private val cordappPackages: List<String> = emptyList(), private val
|
||||
}
|
||||
}
|
||||
|
||||
class InProcessNode(configuration: NodeConfiguration, versionInfo: VersionInfo, flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides)) : Node(configuration, versionInfo, false, flowManager = flowManager) {
|
||||
class InProcessNode(
|
||||
configuration: NodeConfiguration,
|
||||
versionInfo: VersionInfo,
|
||||
flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides),
|
||||
allowHibernateToManageAppSchema: Boolean = true) : Node(configuration, versionInfo, false, flowManager = flowManager, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema) {
|
||||
override val runMigrationScripts: Boolean = true
|
||||
override fun start(): NodeInfo {
|
||||
assertFalse(isInvalidJavaVersion(), "You are using a version of Java that is not supported (${SystemUtils.JAVA_VERSION}). Please upgrade to the latest version of Java 8.")
|
||||
return super.start()
|
||||
|
@ -163,6 +163,7 @@ fun RPCSecurityManagerImpl.Companion.fromUserList(id: AuthServiceId, users: List
|
||||
/**
|
||||
* Convenience method for configuring a database for some tests.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
fun configureDatabase(hikariProperties: Properties,
|
||||
databaseConfig: DatabaseConfig,
|
||||
wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
|
||||
@ -170,9 +171,19 @@ fun configureDatabase(hikariProperties: Properties,
|
||||
schemaService: SchemaService = NodeSchemaService(),
|
||||
internalSchemas: Set<MappedSchema> = NodeSchemaService().internalSchemas(),
|
||||
cacheFactory: NamedCacheFactory = TestingNamedCacheFactory(),
|
||||
ourName: CordaX500Name = TestIdentity(ALICE_NAME, 70).name): CordaPersistence {
|
||||
val persistence = createCordaPersistence(databaseConfig, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous, schemaService, hikariProperties, cacheFactory, null)
|
||||
persistence.startHikariPool(hikariProperties, databaseConfig, internalSchemas, ourName = ourName)
|
||||
ourName: CordaX500Name = TestIdentity(ALICE_NAME, 70).name,
|
||||
runMigrationScripts: Boolean = true,
|
||||
allowHibernateToManageAppSchema: Boolean = true): CordaPersistence {
|
||||
val persistence = createCordaPersistence(
|
||||
databaseConfig,
|
||||
wellKnownPartyFromX500Name,
|
||||
wellKnownPartyFromAnonymous,
|
||||
schemaService,
|
||||
hikariProperties,
|
||||
cacheFactory,
|
||||
null,
|
||||
allowHibernateToManageAppSchema)
|
||||
persistence.startHikariPool(hikariProperties, internalSchemas, ourName = ourName, runMigrationScripts = runMigrationScripts)
|
||||
return persistence
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user