mirror of
https://github.com/corda/corda.git
synced 2025-06-01 23:20:54 +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
@ -9,4 +9,4 @@ package net.corda.common.logging
|
|||||||
* (originally added to source control for ease of use)
|
* (originally added to source control for ease of use)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
internal const val CURRENT_MAJOR_RELEASE = "4.6-SNAPSHOT"
|
internal const val CURRENT_MAJOR_RELEASE = "4.6-SNAPSHOT"
|
@ -112,7 +112,7 @@ class FlowIsKilledTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout = 300_000)
|
@Test(timeout = 300_000)
|
||||||
fun `manually handle killed flows using checkFlowIsNotKilled`() {
|
fun `manually handle killed flows using checkForIsNotKilled`() {
|
||||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
alice.rpc.let { rpc ->
|
alice.rpc.let { rpc ->
|
||||||
@ -131,7 +131,7 @@ class FlowIsKilledTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout = 300_000)
|
@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)) {
|
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
alice.rpc.let { rpc ->
|
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.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentState
|
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.internal.DataSourceFactory
|
||||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
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.ALICE_NAME
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
@ -41,8 +40,7 @@ class MissingSchemaMigrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createSchemaMigration(schemasToMigrate: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean): SchemaMigration {
|
private fun createSchemaMigration(schemasToMigrate: Set<MappedSchema>, forceThrowOnMissingMigration: Boolean): SchemaMigration {
|
||||||
val databaseConfig = DatabaseConfig()
|
return SchemaMigration(schemasToMigrate, dataSource, null, null,
|
||||||
return SchemaMigration(schemasToMigrate, dataSource, databaseConfig, null, null,
|
|
||||||
TestIdentity(ALICE_NAME, 70).name, forceThrowOnMissingMigration)
|
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`() {
|
fun `test that an error is thrown when forceThrowOnMissingMigration is set and a mapped schema is missing a migration`() {
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
createSchemaMigration(setOf(GoodSchema), true)
|
createSchemaMigration(setOf(GoodSchema), true)
|
||||||
.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
.runMigration(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||||
}.isInstanceOf(MissingMigrationException::class.java)
|
}.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`() {
|
fun `test that an error is not thrown when forceThrowOnMissingMigration is not set and a mapped schema is missing a migration`() {
|
||||||
assertDoesNotThrow {
|
assertDoesNotThrow {
|
||||||
createSchemaMigration(setOf(GoodSchema), false)
|
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 " +
|
assertDoesNotThrow("This test failure indicates " +
|
||||||
"a new table has been added to the node without the appropriate migration scripts being present") {
|
"a new table has been added to the node without the appropriate migration scripts being present") {
|
||||||
createSchemaMigration(NodeSchemaService().internalSchemas(), false)
|
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"
|
"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 const val LOGS_DIR_NAME = "logs"
|
||||||
|
|
||||||
private val jarsThatArentCordapps = setOf("corda.jar", "runnodes.jar")
|
private val jarsThatArentCordapps = setOf("corda.jar", "runnodes.jar")
|
||||||
@ -92,7 +99,9 @@ constructor(private val initSerEnv: Boolean,
|
|||||||
}
|
}
|
||||||
val executor = Executors.newFixedThreadPool(numParallelProcesses)
|
val executor = Executors.newFixedThreadPool(numParallelProcesses)
|
||||||
return try {
|
return try {
|
||||||
nodeDirs.map { executor.fork { generateNodeInfo(it) } }.transpose().getOrThrow()
|
nodeDirs.map { executor.fork {
|
||||||
|
createDbSchemas(it)
|
||||||
|
generateNodeInfo(it) } }.transpose().getOrThrow()
|
||||||
} finally {
|
} finally {
|
||||||
warningTimer.cancel()
|
warningTimer.cancel()
|
||||||
executor.shutdownNow()
|
executor.shutdownNow()
|
||||||
@ -100,23 +109,31 @@ constructor(private val initSerEnv: Boolean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateNodeInfo(nodeDir: Path): Path {
|
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 logsDir = (nodeDir / LOGS_DIR_NAME).createDirectories()
|
||||||
val nodeInfoGenFile = (logsDir / "node-info-gen.log").toFile()
|
val nodeRedirectFile = (logsDir / logfileName).toFile()
|
||||||
val process = ProcessBuilder(nodeInfoGenCmd)
|
val process = ProcessBuilder(command)
|
||||||
.directory(nodeDir.toFile())
|
.directory(nodeDir.toFile())
|
||||||
.redirectErrorStream(true)
|
.redirectErrorStream(true)
|
||||||
.redirectOutput(nodeInfoGenFile)
|
.redirectOutput(nodeRedirectFile)
|
||||||
.apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" }
|
.apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" }
|
||||||
.start()
|
.start()
|
||||||
try {
|
try {
|
||||||
if (!process.waitFor(3, TimeUnit.MINUTES)) {
|
if (!process.waitFor(3, TimeUnit.MINUTES)) {
|
||||||
process.destroyForcibly()
|
process.destroyForcibly()
|
||||||
printNodeInfoGenLogToConsole(nodeInfoGenFile)
|
printNodeOutputToConsoleAndThrow(nodeRedirectFile)
|
||||||
}
|
|
||||||
printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 }
|
|
||||||
return nodeDir.list { paths ->
|
|
||||||
paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
|
||||||
}
|
}
|
||||||
|
if (process.exitValue() != 0) printNodeOutputToConsoleAndThrow(nodeRedirectFile)
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
// Don't leave this process dangling if the thread is interrupted.
|
// Don't leave this process dangling if the thread is interrupted.
|
||||||
process.destroyForcibly()
|
process.destroyForcibly()
|
||||||
@ -124,18 +141,16 @@ constructor(private val initSerEnv: Boolean,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun printNodeInfoGenLogToConsole(nodeInfoGenFile: File, check: (() -> Boolean) = { true }) {
|
private fun printNodeOutputToConsoleAndThrow(stdoutFile: File) {
|
||||||
if (!check.invoke()) {
|
val nodeDir = stdoutFile.parent
|
||||||
val nodeDir = nodeInfoGenFile.parent
|
val nodeIdentifier = try {
|
||||||
val nodeIdentifier = try {
|
ConfigFactory.parseFile((nodeDir / "node.conf").toFile()).getString("myLegalName")
|
||||||
ConfigFactory.parseFile((nodeDir / "node.conf").toFile()).getString("myLegalName")
|
} catch (e: ConfigException) {
|
||||||
} catch (e: ConfigException) {
|
nodeDir
|
||||||
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.")
|
|
||||||
}
|
}
|
||||||
|
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
|
const val DEFAULT_MAX_MESSAGE_SIZE: Int = 10485760
|
||||||
|
@ -31,24 +31,12 @@ import javax.sql.DataSource
|
|||||||
*/
|
*/
|
||||||
const val NODE_DATABASE_PREFIX = "node_"
|
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
|
// This class forms part of the node config and so any changes to it must be handled with care
|
||||||
data class DatabaseConfig(
|
data class DatabaseConfig(
|
||||||
val initialiseSchema: Boolean = Defaults.initialiseSchema,
|
|
||||||
val initialiseAppSchema: SchemaInitializationType = Defaults.initialiseAppSchema,
|
|
||||||
val transactionIsolationLevel: TransactionIsolationLevel = Defaults.transactionIsolationLevel,
|
|
||||||
val exportHibernateJMXStatistics: Boolean = Defaults.exportHibernateJMXStatistics,
|
val exportHibernateJMXStatistics: Boolean = Defaults.exportHibernateJMXStatistics,
|
||||||
val mappedSchemaCacheSize: Long = Defaults.mappedSchemaCacheSize
|
val mappedSchemaCacheSize: Long = Defaults.mappedSchemaCacheSize
|
||||||
) {
|
) {
|
||||||
object Defaults {
|
object Defaults {
|
||||||
val initialiseSchema = true
|
|
||||||
val initialiseAppSchema = SchemaInitializationType.UPDATE
|
|
||||||
val transactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
|
||||||
val exportHibernateJMXStatistics = false
|
val exportHibernateJMXStatistics = false
|
||||||
val mappedSchemaCacheSize = 100L
|
val mappedSchemaCacheSize = 100L
|
||||||
}
|
}
|
||||||
@ -67,6 +55,10 @@ enum class TransactionIsolationLevel {
|
|||||||
*/
|
*/
|
||||||
val jdbcString = "TRANSACTION_$name"
|
val jdbcString = "TRANSACTION_$name"
|
||||||
val jdbcValue: Int = java.sql.Connection::class.java.getField(jdbcString).get(null) as Int
|
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 }
|
internal val _prohibitDatabaseAccess = ThreadLocal.withInitial { false }
|
||||||
@ -103,20 +95,21 @@ class CordaPersistence(
|
|||||||
attributeConverters: Collection<AttributeConverter<*, *>> = emptySet(),
|
attributeConverters: Collection<AttributeConverter<*, *>> = emptySet(),
|
||||||
customClassLoader: ClassLoader? = null,
|
customClassLoader: ClassLoader? = null,
|
||||||
val closeConnection: Boolean = true,
|
val closeConnection: Boolean = true,
|
||||||
val errorHandler: DatabaseTransaction.(e: Exception) -> Unit = {}
|
val errorHandler: DatabaseTransaction.(e: Exception) -> Unit = {},
|
||||||
|
allowHibernateToManageAppSchema: Boolean = false
|
||||||
) : Closeable {
|
) : Closeable {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val defaultIsolationLevel = databaseConfig.transactionIsolationLevel
|
private val defaultIsolationLevel = TransactionIsolationLevel.default
|
||||||
val hibernateConfig: HibernateConfiguration by lazy {
|
val hibernateConfig: HibernateConfiguration by lazy {
|
||||||
transaction {
|
transaction {
|
||||||
try {
|
try {
|
||||||
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader)
|
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader, allowHibernateToManageAppSchema)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
when (e) {
|
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)
|
else -> throw HibernateConfigException("Could not create Hibernate configuration: ${e.message}", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ class HibernateConfiguration(
|
|||||||
private val attributeConverters: Collection<AttributeConverter<*, *>>,
|
private val attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||||
jdbcUrl: String,
|
jdbcUrl: String,
|
||||||
cacheFactory: NamedCacheFactory,
|
cacheFactory: NamedCacheFactory,
|
||||||
val customClassLoader: ClassLoader? = null
|
val customClassLoader: ClassLoader? = null,
|
||||||
|
val allowHibernateToManageAppSchema: Boolean = false
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = contextLogger()
|
private val logger = contextLogger()
|
||||||
@ -64,7 +65,7 @@ class HibernateConfiguration(
|
|||||||
fun sessionFactoryForSchemas(key: Set<MappedSchema>): SessionFactory = sessionFactories.get(key, ::makeSessionFactoryForSchemas)!!
|
fun sessionFactoryForSchemas(key: Set<MappedSchema>): SessionFactory = sessionFactories.get(key, ::makeSessionFactoryForSchemas)!!
|
||||||
|
|
||||||
private fun makeSessionFactoryForSchemas(schemas: Set<MappedSchema>): SessionFactory {
|
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
|
// export Hibernate JMX statistics
|
||||||
if (databaseConfig.exportHibernateJMXStatistics)
|
if (databaseConfig.exportHibernateJMXStatistics)
|
||||||
|
@ -25,7 +25,6 @@ import kotlin.concurrent.withLock
|
|||||||
class SchemaMigration(
|
class SchemaMigration(
|
||||||
val schemas: Set<MappedSchema>,
|
val schemas: Set<MappedSchema>,
|
||||||
val dataSource: DataSource,
|
val dataSource: DataSource,
|
||||||
private val databaseConfig: DatabaseConfig,
|
|
||||||
cordappLoader: CordappLoader? = null,
|
cordappLoader: CordappLoader? = null,
|
||||||
private val currentDirectory: Path?,
|
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
|
// 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
|
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.
|
* 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.
|
* 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. */
|
/** 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) {
|
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)) {
|
class DatabaseIncompatibleException(@Suppress("MemberVisibilityCanBePrivate") private val reason: String) : DatabaseMigrationException(errorMessageFor(reason)) {
|
||||||
internal companion object {
|
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.core.utilities.toHexString
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
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.SessionFactory
|
||||||
import org.hibernate.boot.Metadata
|
import org.hibernate.boot.Metadata
|
||||||
import org.hibernate.boot.MetadataBuilder
|
import org.hibernate.boot.MetadataBuilder
|
||||||
@ -26,22 +26,19 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
|||||||
private val logger = contextLogger()
|
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 =
|
val hbm2dll: String =
|
||||||
if (databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE) {
|
if (allowHibernateToManageAppSchema) {
|
||||||
"update"
|
"update"
|
||||||
} else if ((!databaseConfig.initialiseSchema && databaseConfig.initialiseAppSchema == SchemaInitializationType.UPDATE)
|
} else {
|
||||||
|| databaseConfig.initialiseAppSchema == SchemaInitializationType.VALIDATE) {
|
|
||||||
"validate"
|
"validate"
|
||||||
} else {
|
|
||||||
"none"
|
|
||||||
}
|
}
|
||||||
// We set a connection provider as the auto schema generation requires it. The auto schema generation will not
|
// 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.
|
// 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)
|
return Configuration(metadataSources).setProperty("hibernate.connection.provider_class", HibernateConfiguration.NodeDatabaseConnectionProvider::class.java.name)
|
||||||
.setProperty("hibernate.format_sql", "true")
|
.setProperty("hibernate.format_sql", "true")
|
||||||
.setProperty("javax.persistence.validation.mode", "none")
|
.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.hbm2ddl.auto", hbm2dll)
|
||||||
.setProperty("hibernate.jdbc.time_zone", "UTC")
|
.setProperty("hibernate.jdbc.time_zone", "UTC")
|
||||||
}
|
}
|
||||||
@ -88,12 +85,13 @@ abstract class BaseSessionFactoryFactory : CordaSessionFactoryFactory {
|
|||||||
databaseConfig: DatabaseConfig,
|
databaseConfig: DatabaseConfig,
|
||||||
schemas: Set<MappedSchema>,
|
schemas: Set<MappedSchema>,
|
||||||
customClassLoader: ClassLoader?,
|
customClassLoader: ClassLoader?,
|
||||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory {
|
attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||||
|
allowHibernateToMananageAppSchema: Boolean): SessionFactory {
|
||||||
logger.info("Creating session factory for schemas: $schemas")
|
logger.info("Creating session factory for schemas: $schemas")
|
||||||
val serviceRegistry = BootstrapServiceRegistryBuilder().build()
|
val serviceRegistry = BootstrapServiceRegistryBuilder().build()
|
||||||
val metadataSources = MetadataSources(serviceRegistry)
|
val metadataSources = MetadataSources(serviceRegistry)
|
||||||
|
|
||||||
val config = buildHibernateConfig(databaseConfig, metadataSources)
|
val config = buildHibernateConfig(databaseConfig, metadataSources, allowHibernateToMananageAppSchema)
|
||||||
schemas.forEach { schema ->
|
schemas.forEach { schema ->
|
||||||
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
schema.mappedTypes.forEach { config.addAnnotatedClass(it) }
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ interface CordaSessionFactoryFactory {
|
|||||||
databaseConfig: DatabaseConfig,
|
databaseConfig: DatabaseConfig,
|
||||||
schemas: Set<MappedSchema>,
|
schemas: Set<MappedSchema>,
|
||||||
customClassLoader: ClassLoader?,
|
customClassLoader: ClassLoader?,
|
||||||
attributeConverters: Collection<AttributeConverter<*, *>>): SessionFactory
|
attributeConverters: Collection<AttributeConverter<*, *>>,
|
||||||
|
allowHibernateToMananageAppSchema: Boolean): SessionFactory
|
||||||
fun getExtraConfiguration(key: String): Any?
|
fun getExtraConfiguration(key: String): Any?
|
||||||
fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata
|
fun buildHibernateMetadata(metadataBuilder: MetadataBuilder, attributeConverters: Collection<AttributeConverter<*, *>>): Metadata
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.serialization.reproduction;
|
package net.corda.serialization.reproduction;
|
||||||
|
|
||||||
|
import com.google.common.io.LineProcessor;
|
||||||
import net.corda.client.rpc.CordaRPCClient;
|
import net.corda.client.rpc.CordaRPCClient;
|
||||||
import net.corda.core.concurrent.CordaFuture;
|
import net.corda.core.concurrent.CordaFuture;
|
||||||
import net.corda.node.services.Permissions;
|
import net.corda.node.services.Permissions;
|
||||||
|
@ -44,7 +44,7 @@ class BootTests {
|
|||||||
rpc.startFlow(::ObjectInputStreamFlow).returnValue.getOrThrow()
|
rpc.startFlow(::ObjectInputStreamFlow).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
driver(DriverParameters(cordappsForAllNodes = listOf(enclosedCordapp()))) {
|
driver(DriverParameters(cordappsForAllNodes = listOf(enclosedCordapp()), allowHibernateToManageAppSchema = false)) {
|
||||||
val devModeNode = startNode(devParams).getOrThrow()
|
val devModeNode = startNode(devParams).getOrThrow()
|
||||||
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import javax.security.auth.x500.X500Principal
|
|||||||
class NodeKeystoreCheckTest {
|
class NodeKeystoreCheckTest {
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `starting node in non-dev mode with no key store`() {
|
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 {
|
assertThatThrownBy {
|
||||||
startNode(customOverrides = mapOf("devMode" to false)).getOrThrow()
|
startNode(customOverrides = mapOf("devMode" to false)).getOrThrow()
|
||||||
}.hasMessageContaining("One or more keyStores (identity or TLS) or trustStore not found.")
|
}.hasMessageContaining("One or more keyStores (identity or TLS) or trustStore not found.")
|
||||||
@ -26,7 +26,7 @@ class NodeKeystoreCheckTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `node should throw exception if cert path does not chain to the trust root`() {
|
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.
|
// Create keystores.
|
||||||
val keystorePassword = "password"
|
val keystorePassword = "password"
|
||||||
val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
|
val certificatesDirectory = baseDirectory(ALICE_NAME) / "certificates"
|
||||||
|
@ -2,32 +2,21 @@ package net.corda.node.persistence
|
|||||||
|
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.flows.isQuasarAgentSpecified
|
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.DriverParameters
|
||||||
import net.corda.testing.driver.NodeParameters
|
import net.corda.testing.driver.NodeParameters
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
|
|
||||||
class DbSchemaInitialisationTest {
|
class DbSchemaInitialisationTest {
|
||||||
|
@Test(timeout = 300_000)
|
||||||
@Test(timeout=300_000)
|
fun `database initialisation not allowed in config`() {
|
||||||
fun `database is initialised`() {
|
|
||||||
driver(DriverParameters(startNodesInProcess = isQuasarAgentSpecified(), cordappsForAllNodes = emptyList())) {
|
driver(DriverParameters(startNodesInProcess = isQuasarAgentSpecified(), cordappsForAllNodes = emptyList())) {
|
||||||
val nodeHandle = {
|
assertFailsWith(ConfigurationException::class) {
|
||||||
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) {
|
|
||||||
startNode(NodeParameters(customOverrides = mapOf("database.initialiseSchema" to "false"))).getOrThrow()
|
startNode(NodeParameters(customOverrides = mapOf("database.initialiseSchema" to "false"))).getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -92,7 +92,8 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
|
|||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
notarySpecs = emptyList()
|
notarySpecs = emptyList(),
|
||||||
|
allowHibernateToManageAppSchema = false
|
||||||
) {
|
) {
|
||||||
val alice = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow() as NodeHandleInternal
|
val alice = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow() as NodeHandleInternal
|
||||||
val nextParams = networkMapServer.networkParameters.copy(
|
val nextParams = networkMapServer.networkParameters.copy(
|
||||||
@ -146,7 +147,8 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
|
|||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
notarySpecs = emptyList()
|
notarySpecs = emptyList(),
|
||||||
|
allowHibernateToManageAppSchema = false
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow()
|
||||||
assertDownloadedNetworkParameters(aliceNode)
|
assertDownloadedNetworkParameters(aliceNode)
|
||||||
@ -175,7 +177,8 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
|
|||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
notarySpecs = emptyList(),
|
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 aliceNode = startNode(providedName = ALICE_NAME, devMode = false).getOrThrow()
|
||||||
val aliceNodeInfo = aliceNode.nodeInfo.serialize().hash
|
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 devModeNode = startNode(params, BOB_NAME).getOrThrow()
|
||||||
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
val node = startNode(ALICE_NAME, devMode = false, parameters = params).getOrThrow()
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ class RpcExceptionHandlingTest {
|
|||||||
rpc.startFlow(::FlowExceptionFlow, expectedMessage, expectedErrorId).returnValue.getOrThrow()
|
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 devModeNode = startNode(params, BOB_NAME).getOrThrow()
|
||||||
val node = startNode(ALICE_NAME, devMode = false, parameters = params).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()
|
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 ->
|
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()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")
|
findCordapp("com.r3.dbfailure.schemas")
|
||||||
),inMemoryDB = false)
|
), inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val bobNode = startNode(providedName = BOB_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()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")
|
findCordapp("com.r3.dbfailure.schemas")
|
||||||
),
|
),
|
||||||
inMemoryDB = false)
|
inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val bobNode = startNode(providedName = BOB_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()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")
|
findCordapp("com.r3.dbfailure.schemas")
|
||||||
),
|
),
|
||||||
inMemoryDB = false)
|
inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val bobNode = startNode(providedName = BOB_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()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")
|
findCordapp("com.r3.dbfailure.schemas")
|
||||||
),
|
),
|
||||||
inMemoryDB = false)
|
inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
val bobNode = startNode(providedName = BOB_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` () {
|
fun `Accessing NodeVaultService rawUpdates from a flow is not allowed` () {
|
||||||
val user = User("user", "foo", setOf(Permissions.all()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")
|
findCordapp("com.r3.dbfailure.schemas")
|
||||||
),
|
),
|
||||||
inMemoryDB = false)
|
inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
|
|
||||||
@ -771,12 +771,12 @@ class VaultObserverExceptionTest {
|
|||||||
|
|
||||||
val user = User("user", "foo", setOf(Permissions.all()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.transactionfailure.workflows"),
|
findCordapp("com.r3.transactionfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")),
|
findCordapp("com.r3.dbfailure.schemas")),
|
||||||
inMemoryDB = false)
|
inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
|
||||||
|
|
||||||
@ -802,12 +802,12 @@ class VaultObserverExceptionTest {
|
|||||||
|
|
||||||
val user = User("user", "foo", setOf(Permissions.all()))
|
val user = User("user", "foo", setOf(Permissions.all()))
|
||||||
driver(DriverParameters(startNodesInProcess = true,
|
driver(DriverParameters(startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(
|
cordappsForAllNodes = listOf(
|
||||||
findCordapp("com.r3.dbfailure.contracts"),
|
findCordapp("com.r3.dbfailure.contracts"),
|
||||||
findCordapp("com.r3.dbfailure.workflows"),
|
findCordapp("com.r3.dbfailure.workflows"),
|
||||||
findCordapp("com.r3.transactionfailure.workflows"),
|
findCordapp("com.r3.transactionfailure.workflows"),
|
||||||
findCordapp("com.r3.dbfailure.schemas")),
|
findCordapp("com.r3.dbfailure.schemas")),
|
||||||
inMemoryDB = false)
|
inMemoryDB = false)
|
||||||
) {
|
) {
|
||||||
// Subscribing with custom SafeSubscriber; the custom SafeSubscriber will not get replaced by a ResilientSubscriber
|
// 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.
|
// 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
|
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> {
|
open fun parseConfiguration(configuration: Config): Valid<NodeConfiguration> {
|
||||||
val option = Configuration.Options(strict = unknownConfigKeysPolicy == UnknownConfigKeysPolicy.FAIL)
|
val option = Configuration.Options(strict = unknownConfigKeysPolicy == UnknownConfigKeysPolicy.FAIL)
|
||||||
return configuration.parseAsNodeConfiguration(option)
|
return configuration.parseAsNodeConfiguration(option)
|
||||||
|
@ -210,7 +210,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
||||||
val busyNodeLatch: ReusableLatch = ReusableLatch(),
|
val busyNodeLatch: ReusableLatch = ReusableLatch(),
|
||||||
djvmBootstrapSource: ApiSource = EmptyApi,
|
djvmBootstrapSource: ApiSource = EmptyApi,
|
||||||
djvmCordaSource: UserSource? = null) : SingletonSerializeAsToken() {
|
djvmCordaSource: UserSource? = null,
|
||||||
|
protected val allowHibernateToManageAppSchema: Boolean = false) : SingletonSerializeAsToken() {
|
||||||
|
|
||||||
protected abstract val log: Logger
|
protected abstract val log: Logger
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
@ -222,6 +223,11 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
protected val runOnStop = ArrayList<() -> Any?>()
|
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 {
|
init {
|
||||||
(serverThread as? ExecutorService)?.let {
|
(serverThread as? ExecutorService)?.let {
|
||||||
runOnStop += {
|
runOnStop += {
|
||||||
@ -233,6 +239,12 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
quasarExcludePackages(configuration)
|
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 {
|
private val notaryLoader = configuration.notary?.let {
|
||||||
@ -248,7 +260,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
schemaService,
|
schemaService,
|
||||||
configuration.dataSourceProperties,
|
configuration.dataSourceProperties,
|
||||||
cacheFactory,
|
cacheFactory,
|
||||||
cordappLoader.appClassLoader)
|
cordappLoader.appClassLoader,
|
||||||
|
allowHibernateToManageAppSchema)
|
||||||
|
|
||||||
private val transactionSupport = CordaTransactionSupportImpl(database)
|
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 {
|
open fun start(): S {
|
||||||
check(started == null) { "Node has already been started" }
|
check(started == null) { "Node has already been started" }
|
||||||
|
|
||||||
@ -946,7 +986,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
protected open fun startDatabase() {
|
protected open fun startDatabase() {
|
||||||
val props = configuration.dataSourceProperties
|
val props = configuration.dataSourceProperties
|
||||||
if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.")
|
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.
|
// Now log the vendor string as this will also cause a connection to be tested eagerly.
|
||||||
logVendorString(database, log)
|
logVendorString(database, log)
|
||||||
}
|
}
|
||||||
@ -1313,13 +1353,15 @@ class FlowStarterImpl(private val smm: StateMachineManager, private val flowLogi
|
|||||||
|
|
||||||
class ConfigurationException(message: String) : CordaException(message)
|
class ConfigurationException(message: String) : CordaException(message)
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
fun createCordaPersistence(databaseConfig: DatabaseConfig,
|
fun createCordaPersistence(databaseConfig: DatabaseConfig,
|
||||||
wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
|
wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
|
||||||
wellKnownPartyFromAnonymous: (AbstractParty) -> Party?,
|
wellKnownPartyFromAnonymous: (AbstractParty) -> Party?,
|
||||||
schemaService: SchemaService,
|
schemaService: SchemaService,
|
||||||
hikariProperties: Properties,
|
hikariProperties: Properties,
|
||||||
cacheFactory: NamedCacheFactory,
|
cacheFactory: NamedCacheFactory,
|
||||||
customClassLoader: ClassLoader?): CordaPersistence {
|
customClassLoader: ClassLoader?,
|
||||||
|
allowHibernateToManageAppSchema: Boolean = false): CordaPersistence {
|
||||||
// Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately
|
// 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
|
// 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
|
// 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", "")
|
val jdbcUrl = hikariProperties.getProperty("dataSource.url", "")
|
||||||
return CordaPersistence(
|
return CordaPersistence(
|
||||||
databaseConfig,
|
databaseConfig,
|
||||||
schemaService.schemas,
|
schemaService.schemas,
|
||||||
jdbcUrl,
|
jdbcUrl,
|
||||||
cacheFactory,
|
cacheFactory,
|
||||||
attributeConverters, customClassLoader,
|
attributeConverters, customClassLoader,
|
||||||
errorHandler = { e ->
|
errorHandler = { e ->
|
||||||
// "corrupting" a DatabaseTransaction only inside a flow state machine execution
|
// "corrupting" a DatabaseTransaction only inside a flow state machine execution
|
||||||
FlowStateMachineImpl.currentStateMachine()?.let {
|
FlowStateMachineImpl.currentStateMachine()?.let {
|
||||||
// register only the very first exception thrown throughout a chain of logical transactions
|
// register only the very first exception thrown throughout a chain of logical transactions
|
||||||
setException(e)
|
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 {
|
try {
|
||||||
val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry)
|
val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry)
|
||||||
val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, cordappLoader, currentDir, ourName)
|
val schemaMigration = SchemaMigration(schemas, dataSource, cordappLoader, currentDir, ourName)
|
||||||
schemaMigration.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
if (runMigrationScripts) {
|
||||||
|
schemaMigration.runMigration(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
|
||||||
|
} else {
|
||||||
|
schemaMigration.checkState()
|
||||||
|
}
|
||||||
start(dataSource)
|
start(dataSource)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
when {
|
when {
|
||||||
|
@ -125,7 +125,8 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides),
|
flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides),
|
||||||
cacheFactoryPrototype: BindableNamedCacheFactory = DefaultNamedCacheFactory(),
|
cacheFactoryPrototype: BindableNamedCacheFactory = DefaultNamedCacheFactory(),
|
||||||
djvmBootstrapSource: ApiSource = createBootstrapSource(configuration),
|
djvmBootstrapSource: ApiSource = createBootstrapSource(configuration),
|
||||||
djvmCordaSource: UserSource? = createCordaSource(configuration)
|
djvmCordaSource: UserSource? = createCordaSource(configuration),
|
||||||
|
allowHibernateToManageAppSchema: Boolean = false
|
||||||
) : AbstractNode<NodeInfo>(
|
) : AbstractNode<NodeInfo>(
|
||||||
configuration,
|
configuration,
|
||||||
createClock(configuration),
|
createClock(configuration),
|
||||||
@ -135,7 +136,8 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
// Under normal (non-test execution) it will always be "1"
|
// Under normal (non-test execution) it will always be "1"
|
||||||
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1),
|
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1),
|
||||||
djvmBootstrapSource = djvmBootstrapSource,
|
djvmBootstrapSource = djvmBootstrapSource,
|
||||||
djvmCordaSource = djvmCordaSource
|
djvmCordaSource = djvmCordaSource,
|
||||||
|
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): NodeInfo =
|
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): NodeInfo =
|
||||||
@ -559,6 +561,13 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
return super.generateAndSaveNodeInfo()
|
return super.generateAndSaveNodeInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun runDatabaseMigrationScripts() {
|
||||||
|
if (allowHibernateToManageAppSchema) {
|
||||||
|
initialiseSerialization()
|
||||||
|
}
|
||||||
|
super.runDatabaseMigrationScripts()
|
||||||
|
}
|
||||||
|
|
||||||
override fun start(): NodeInfo {
|
override fun start(): NodeInfo {
|
||||||
registerDefaultExceptionHandler()
|
registerDefaultExceptionHandler()
|
||||||
initialiseSerialization()
|
initialiseSerialization()
|
||||||
|
@ -76,10 +76,16 @@ open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") {
|
|||||||
private val justGenerateRpcSslCertsCli by lazy { GenerateRpcSslCertsCli(startup) }
|
private val justGenerateRpcSslCertsCli by lazy { GenerateRpcSslCertsCli(startup) }
|
||||||
private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) }
|
private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) }
|
||||||
private val validateConfigurationCli by lazy { ValidateConfigurationCli() }
|
private val validateConfigurationCli by lazy { ValidateConfigurationCli() }
|
||||||
|
private val runMigrationScriptsCli by lazy { RunMigrationScriptsCli(startup) }
|
||||||
|
|
||||||
override fun initLogging(): Boolean = this.initLogging(cmdLineOptions.baseDirectory)
|
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 {
|
override fun call(): Int {
|
||||||
if (!validateBaseDirectory()) {
|
if (!validateBaseDirectory()) {
|
||||||
@ -201,7 +207,7 @@ open class NodeStartup : NodeStartupLogging {
|
|||||||
|
|
||||||
protected open fun preNetworkRegistration(conf: NodeConfiguration) = Unit
|
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) {
|
fun startNode(node: Node, startTime: Long) {
|
||||||
if (node.configuration.devMode) {
|
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.SslConfiguration
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.SchemaInitializationType
|
|
||||||
import net.corda.tools.shell.SSHDConfiguration
|
import net.corda.tools.shell.SSHDConfiguration
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -129,8 +128,6 @@ data class NodeConfigurationImpl(
|
|||||||
fun messagingServerExternal(messagingServerAddress: NetworkHostAndPort?) = messagingServerAddress != null
|
fun messagingServerExternal(messagingServerAddress: NetworkHostAndPort?) = messagingServerAddress != null
|
||||||
|
|
||||||
fun database(devMode: Boolean) = DatabaseConfig(
|
fun database(devMode: Boolean) = DatabaseConfig(
|
||||||
initialiseSchema = devMode,
|
|
||||||
initialiseAppSchema = if(devMode) SchemaInitializationType.UPDATE else SchemaInitializationType.VALIDATE,
|
|
||||||
exportHibernateJMXStatistics = devMode
|
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.common.validation.internal.Validated.Companion.valid
|
||||||
import net.corda.core.context.AuthServiceId
|
import net.corda.core.context.AuthServiceId
|
||||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
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.AuthDataSourceType
|
||||||
import net.corda.node.services.config.CertChainPolicyConfig
|
import net.corda.node.services.config.CertChainPolicyConfig
|
||||||
import net.corda.node.services.config.CertChainPolicyType
|
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.config.User
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.TransactionIsolationLevel
|
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.bftsmart.BFTSmartConfig
|
||||||
import net.corda.notary.experimental.raft.RaftConfig
|
import net.corda.notary.experimental.raft.RaftConfig
|
||||||
import net.corda.tools.shell.SSHDConfiguration
|
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]) }
|
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") {
|
internal object DatabaseConfigSpec : Configuration.Specification<DatabaseConfig>("DatabaseConfig") {
|
||||||
private val initialiseSchema by boolean().optional().withDefaultValue(DatabaseConfig.Defaults.initialiseSchema)
|
private val initialiseSchema by boolean().optional()
|
||||||
private val initialiseAppSchema by enum(SchemaInitializationType::class).optional().withDefaultValue(DatabaseConfig.Defaults.initialiseAppSchema)
|
private val initialiseAppSchema by enum(SchemaInitializationType::class).optional()
|
||||||
private val transactionIsolationLevel by enum(TransactionIsolationLevel::class).optional().withDefaultValue(DatabaseConfig.Defaults.transactionIsolationLevel)
|
private val transactionIsolationLevel by enum(TransactionIsolationLevel::class).optional()
|
||||||
private val exportHibernateJMXStatistics by boolean().optional().withDefaultValue(DatabaseConfig.Defaults.exportHibernateJMXStatistics)
|
private val exportHibernateJMXStatistics by boolean().optional().withDefaultValue(DatabaseConfig.Defaults.exportHibernateJMXStatistics)
|
||||||
private val mappedSchemaCacheSize by long().optional().withDefaultValue(DatabaseConfig.Defaults.mappedSchemaCacheSize)
|
private val mappedSchemaCacheSize by long().optional().withDefaultValue(DatabaseConfig.Defaults.mappedSchemaCacheSize)
|
||||||
|
|
||||||
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<DatabaseConfig> {
|
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)
|
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 = []
|
additionalP2PAddresses = []
|
||||||
crlCheckSoftFail = true
|
crlCheckSoftFail = true
|
||||||
database = {
|
database = {
|
||||||
transactionIsolationLevel = "REPEATABLE_READ"
|
|
||||||
exportHibernateJMXStatistics = "false"
|
exportHibernateJMXStatistics = "false"
|
||||||
}
|
}
|
||||||
dataSourceProperties = {
|
dataSourceProperties = {
|
||||||
|
@ -7,7 +7,6 @@ import net.corda.node.internal.startHikariPool
|
|||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
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.ALICE_NAME
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||||
@ -91,10 +90,10 @@ class DbMapDeadlockTest {
|
|||||||
|
|
||||||
fun recreateDeadlock(hikariProperties: Properties) {
|
fun recreateDeadlock(hikariProperties: Properties) {
|
||||||
val cacheFactory = TestingNamedCacheFactory()
|
val cacheFactory = TestingNamedCacheFactory()
|
||||||
val dbConfig = DatabaseConfig(initialiseSchema = true, transactionIsolationLevel = TransactionIsolationLevel.READ_COMMITTED)
|
val dbConfig = DatabaseConfig()
|
||||||
val schemaService = NodeSchemaService(extraSchemas = setOf(LockDbSchemaV2))
|
val schemaService = NodeSchemaService(extraSchemas = setOf(LockDbSchemaV2))
|
||||||
createCordaPersistence(dbConfig, { null }, { null }, schemaService, hikariProperties, cacheFactory, null).apply {
|
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 ->
|
}.use { persistence ->
|
||||||
|
|
||||||
// First clean up any remains from previous test runs
|
// 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
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import org.assertj.core.api.Assertions
|
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.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.hibernate.SessionFactory
|
import org.hibernate.SessionFactory
|
||||||
@ -976,7 +977,7 @@ class HibernateConfigurationTest {
|
|||||||
doReturn(it.party).whenever(mock).wellKnownPartyFromX500Name(it.name)
|
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
|
return database
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ dataSourceProperties = {
|
|||||||
dataSource.password = ""
|
dataSource.password = ""
|
||||||
}
|
}
|
||||||
database = {
|
database = {
|
||||||
transactionIsolationLevel = "REPEATABLE_READ"
|
|
||||||
exportHibernateJMXStatistics = "false"
|
exportHibernateJMXStatistics = "false"
|
||||||
}
|
}
|
||||||
p2pAddress = "localhost:2233"
|
p2pAddress = "localhost:2233"
|
||||||
|
@ -12,7 +12,6 @@ dataSourceProperties = {
|
|||||||
dataSource.password = ""
|
dataSource.password = ""
|
||||||
}
|
}
|
||||||
database = {
|
database = {
|
||||||
transactionIsolationLevel = "REPEATABLE_READ"
|
|
||||||
exportHibernateJMXStatistics = "false"
|
exportHibernateJMXStatistics = "false"
|
||||||
}
|
}
|
||||||
p2pAddress = "localhost:2233"
|
p2pAddress = "localhost:2233"
|
||||||
|
@ -202,7 +202,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
|||||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||||
djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
|
djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
|
||||||
djvmCordaSource = defaultParameters.djvmCordaSource,
|
djvmCordaSource = defaultParameters.djvmCordaSource,
|
||||||
environmentVariables = defaultParameters.environmentVariables
|
environmentVariables = defaultParameters.environmentVariables,
|
||||||
|
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
dsl = dsl
|
dsl = dsl
|
||||||
@ -263,7 +264,8 @@ data class DriverParameters(
|
|||||||
val cordappsForAllNodes: Collection<TestCordapp>? = null,
|
val cordappsForAllNodes: Collection<TestCordapp>? = null,
|
||||||
val djvmBootstrapSource: Path? = null,
|
val djvmBootstrapSource: Path? = null,
|
||||||
val djvmCordaSource: List<Path> = emptyList(),
|
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)
|
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 withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource)
|
||||||
fun withDjvmCordaSource(djvmCordaSource: List<Path>): DriverParameters = copy(djvmCordaSource = djvmCordaSource)
|
fun withDjvmCordaSource(djvmCordaSource: List<Path>): DriverParameters = copy(djvmCordaSource = djvmCordaSource)
|
||||||
fun withEnvironmentVariables(variables : Map<String, String>): DriverParameters = copy(environmentVariables = variables)
|
fun withEnvironmentVariables(variables : Map<String, String>): DriverParameters = copy(environmentVariables = variables)
|
||||||
|
fun withAllowHibernateToManageAppSchema(value: Boolean): DriverParameters = copy(allowHibernateToManageAppSchema = value)
|
||||||
|
|
||||||
fun copy(
|
fun copy(
|
||||||
isDebug: Boolean,
|
isDebug: Boolean,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("TooManyFunctions")
|
@file:Suppress("TooManyFunctions", "Deprecation")
|
||||||
package net.corda.testing.node.internal
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.instrument.JavaAgent
|
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.identity.CordaX500Name
|
||||||
import net.corda.core.internal.PLATFORM_VERSION
|
import net.corda.core.internal.PLATFORM_VERSION
|
||||||
import net.corda.core.internal.ThreadBox
|
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.doOnError
|
||||||
import net.corda.core.internal.concurrent.doneFuture
|
import net.corda.core.internal.concurrent.doneFuture
|
||||||
import net.corda.core.internal.concurrent.flatMap
|
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.map
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.internal.concurrent.transpose
|
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_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_VENDOR
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
|
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_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_VENDOR
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION
|
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.MIN_PLATFORM_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.contextLogger
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
|
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
||||||
import net.corda.node.NodeRegistrationOption
|
import net.corda.node.NodeRegistrationOption
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.Node
|
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.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.DUMMY_BANK_A_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.InProcessImpl
|
||||||
import net.corda.testing.driver.internal.NodeHandleInternal
|
import net.corda.testing.driver.internal.NodeHandleInternal
|
||||||
import net.corda.testing.driver.internal.OutOfProcessImpl
|
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.ClusterSpec
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -139,7 +147,8 @@ class DriverDSLImpl(
|
|||||||
val cordappsForAllNodes: Collection<TestCordappInternal>?,
|
val cordappsForAllNodes: Collection<TestCordappInternal>?,
|
||||||
val djvmBootstrapSource: Path?,
|
val djvmBootstrapSource: Path?,
|
||||||
val djvmCordaSource: List<Path>,
|
val djvmCordaSource: List<Path>,
|
||||||
val environmentVariables : Map<String, String>
|
val environmentVariables : Map<String, String>,
|
||||||
|
val allowHibernateToManageAppSchema: Boolean = true
|
||||||
) : InternalDriverDSL {
|
) : InternalDriverDSL {
|
||||||
|
|
||||||
private var _executorService: ScheduledExecutorService? = null
|
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
|
// 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 name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB")
|
||||||
|
|
||||||
|
val config = createConfig(name, parameters, p2pAddress)
|
||||||
val registrationFuture = if (compatibilityZone?.rootCert != null) {
|
val registrationFuture = if (compatibilityZone?.rootCert != null) {
|
||||||
// We don't need the network map to be available to be able to register the node
|
// 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 {
|
} else {
|
||||||
doneFuture(Unit)
|
doneFuture(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
return registrationFuture.flatMap {
|
return registrationFuture.flatMap { conf ->
|
||||||
networkMapAvailability.flatMap {
|
networkMapAvailability.flatMap {networkMap ->
|
||||||
// But starting the node proper does require the network map
|
// But starting the node proper does require the network map
|
||||||
startRegisteredNode(name, it, parameters, p2pAddress, bytemanPort)
|
startRegisteredNode(conf, networkMap, parameters, bytemanPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ComplexMethod")
|
@Suppress("ComplexMethod")
|
||||||
private fun startRegisteredNode(name: CordaX500Name,
|
private fun startRegisteredNode(config: NodeConfig,
|
||||||
localNetworkMap: LocalNetworkMap?,
|
localNetworkMap: LocalNetworkMap?,
|
||||||
parameters: NodeParameters,
|
parameters: NodeParameters,
|
||||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
|
|
||||||
bytemanPort: Int? = null): CordaFuture<NodeHandle> {
|
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 rpcAddress = portAllocation.nextHostAndPort()
|
||||||
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
||||||
val webAddress = portAllocation.nextHostAndPort()
|
|
||||||
val users = parameters.rpcUsers.map { it.copy(permissions = it.permissions + DRIVER_REQUIRED_PERMISSIONS) }
|
val users = parameters.rpcUsers.map { it.copy(permissions = it.permissions + DRIVER_REQUIRED_PERMISSIONS) }
|
||||||
val czUrlConfig = when (compatibilityZone) {
|
val czUrlConfig = when (compatibilityZone) {
|
||||||
null -> emptyMap()
|
null -> emptyMap()
|
||||||
@ -292,50 +311,41 @@ class DriverDSLImpl(
|
|||||||
val flowOverrideConfig = FlowOverrideConfig(parameters.flowOverrides.map { FlowOverride(it.key.canonicalName, it.value.canonicalName) })
|
val flowOverrideConfig = FlowOverrideConfig(parameters.flowOverrides.map { FlowOverride(it.key.canonicalName, it.value.canonicalName) })
|
||||||
|
|
||||||
val overrides = configOf(
|
val overrides = configOf(
|
||||||
NodeConfiguration::myLegalName.name to name.toString(),
|
NodeConfiguration::myLegalName.name to providedName.toString(),
|
||||||
NodeConfiguration::p2pAddress.name to p2pAddress.toString(),
|
NodeConfiguration::p2pAddress.name to p2pAddress.toString(),
|
||||||
"rpcSettings.address" to rpcAddress.toString(),
|
"rpcSettings.address" to rpcAddress.toString(),
|
||||||
"rpcSettings.adminAddress" to rpcAdminAddress.toString(),
|
"rpcSettings.adminAddress" to rpcAdminAddress.toString(),
|
||||||
NodeConfiguration::useTestClock.name to useTestClock,
|
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::verifierType.name to parameters.verifierType.name,
|
||||||
NodeConfiguration::flowOverrides.name to flowOverrideConfig.toConfig().root().unwrapped(),
|
NodeConfiguration::flowOverrides.name to flowOverrideConfig.toConfig().root().unwrapped(),
|
||||||
NodeConfiguration::additionalNodeInfoPollingFrequencyMsec.name to 1000
|
NodeConfiguration::additionalNodeInfoPollingFrequencyMsec.name to 1000
|
||||||
) + czUrlConfig + jmxConfig + parameters.customOverrides
|
) + czUrlConfig + jmxConfig + parameters.customOverrides
|
||||||
val config = NodeConfig(
|
return NodeConfig(
|
||||||
ConfigHelper.loadConfig(
|
ConfigHelper.loadConfig(
|
||||||
baseDirectory = baseDirectory(name),
|
baseDirectory = baseDirectory,
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
|
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
|
||||||
).withDJVMConfig(djvmBootstrapSource, djvmCordaSource)
|
).withDJVMConfig(djvmBootstrapSource, djvmCordaSource)
|
||||||
).checkAndOverrideForInMemoryDB()
|
).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(
|
private fun startNodeRegistration(
|
||||||
providedName: CordaX500Name,
|
config: NodeConfig,
|
||||||
rootCert: X509Certificate,
|
rootCert: X509Certificate,
|
||||||
networkServicesConfig: NetworkServicesConfig,
|
networkServicesConfig: NetworkServicesConfig
|
||||||
customOverrides: Map<String, Any?> = mapOf()
|
|
||||||
): CordaFuture<NodeConfig> {
|
): 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")
|
val versionInfo = VersionInfo(PLATFORM_VERSION, "1", "1", "1")
|
||||||
config.corda.certificatesDirectory.createDirectories()
|
config.corda.certificatesDirectory.createDirectories()
|
||||||
@ -410,7 +420,7 @@ class DriverDSLImpl(
|
|||||||
val notaryInfosFuture = if (compatibilityZone == null) {
|
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
|
// If no CZ is specified then the driver does the generation of the network parameters and the copying of the
|
||||||
// node info files.
|
// node info files.
|
||||||
startNotaryIdentityGeneration().map { notaryInfos -> Pair(notaryInfos, LocalNetworkMap(notaryInfos)) }
|
startNotaryIdentityGeneration().map { notaryInfos -> Pair(notaryInfos, LocalNetworkMap(notaryInfos.map{it.second})) }
|
||||||
} else {
|
} 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.
|
// 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) {
|
val notaryInfosFuture = if (compatibilityZone.rootCert == null) {
|
||||||
@ -421,7 +431,7 @@ class DriverDSLImpl(
|
|||||||
startAllNotaryRegistrations(compatibilityZone.rootCert, compatibilityZone)
|
startAllNotaryRegistrations(compatibilityZone.rootCert, compatibilityZone)
|
||||||
}
|
}
|
||||||
notaryInfosFuture.map { notaryInfos ->
|
notaryInfosFuture.map { notaryInfos ->
|
||||||
compatibilityZone.publishNotaries(notaryInfos)
|
compatibilityZone.publishNotaries(notaryInfos.map{it.second})
|
||||||
Pair(notaryInfos, null)
|
Pair(notaryInfos, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,9 +439,9 @@ class DriverDSLImpl(
|
|||||||
networkMapAvailability = notaryInfosFuture.map { it.second }
|
networkMapAvailability = notaryInfosFuture.map { it.second }
|
||||||
|
|
||||||
_notaries = notaryInfosFuture.map { (notaryInfos, localNetworkMap) ->
|
_notaries = notaryInfosFuture.map { (notaryInfos, localNetworkMap) ->
|
||||||
val listOfFutureNodeHandles = startNotaries(localNetworkMap, notaryCustomOverrides)
|
val listOfFutureNodeHandles = startNotaries(notaryInfos.map{it.first}, localNetworkMap, notaryCustomOverrides)
|
||||||
notaryInfos.zip(listOfFutureNodeHandles) { (identity, validating), nodeHandlesFuture ->
|
notaryInfos.zip(listOfFutureNodeHandles) { (_, notaryInfo), nodeHandlesFuture ->
|
||||||
NotaryHandle(identity, validating, nodeHandlesFuture)
|
NotaryHandle(notaryInfo.identity, notaryInfo.validating, nodeHandlesFuture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -471,9 +481,12 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startNotaryIdentityGeneration(): CordaFuture<List<NotaryInfo>> {
|
private fun startNotaryIdentityGeneration(): CordaFuture<List<Pair<NodeConfig,NotaryInfo>>> {
|
||||||
return executorService.fork {
|
return executorService.fork {
|
||||||
notarySpecs.map { spec ->
|
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) {
|
val identity = when (spec.cluster) {
|
||||||
null -> {
|
null -> {
|
||||||
DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(spec.name), spec.name)
|
DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(spec.name), spec.name)
|
||||||
@ -499,14 +512,14 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
else -> throw UnsupportedOperationException("Cluster spec ${spec.cluster} not supported by Driver")
|
else -> throw UnsupportedOperationException("Cluster spec ${spec.cluster} not supported by Driver")
|
||||||
}
|
}
|
||||||
NotaryInfo(identity, spec.validating)
|
Pair(config, NotaryInfo(identity, spec.validating))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startAllNotaryRegistrations(
|
private fun startAllNotaryRegistrations(
|
||||||
rootCert: X509Certificate,
|
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.
|
// Start the registration process for all the notaries together then wait for their responses.
|
||||||
return notarySpecs.map { spec ->
|
return notarySpecs.map { spec ->
|
||||||
require(spec.cluster == null) { "Registering distributed notaries not supported" }
|
require(spec.cluster == null) { "Registering distributed notaries not supported" }
|
||||||
@ -518,51 +531,56 @@ class DriverDSLImpl(
|
|||||||
spec: NotarySpec,
|
spec: NotarySpec,
|
||||||
rootCert: X509Certificate,
|
rootCert: X509Certificate,
|
||||||
compatibilityZone: CompatibilityZoneParams
|
compatibilityZone: CompatibilityZoneParams
|
||||||
): CordaFuture<NotaryInfo> {
|
): CordaFuture<Pair<NodeConfig,NotaryInfo>> {
|
||||||
return startNodeRegistration(spec.name, rootCert, compatibilityZone.config()).flatMap { config ->
|
val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating))
|
||||||
// Node registration only gives us the node CA cert, not the identity cert. That is only created on first
|
val parameters = NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig + notaryCustomOverrides, maximumHeapSize = spec.maximumHeapSize)
|
||||||
// startup or when the node is told to just generate its node info file. We do that here.
|
return createSchema(createConfig(spec.name, parameters), false).doOnComplete { config ->
|
||||||
if (startNodesInProcess) {
|
startNodeRegistration(config, rootCert, compatibilityZone.config())}.flatMap { config ->
|
||||||
executorService.fork {
|
// Node registration only gives us the node CA cert, not the identity cert. That is only created on first
|
||||||
val nodeInfo = Node(config.corda, MOCK_VERSION_INFO, initialiseSerialization = false).generateAndSaveNodeInfo()
|
// startup or when the node is told to just generate its node info file. We do that here.
|
||||||
NotaryInfo(nodeInfo.legalIdentities[0], spec.validating)
|
if (startNodesInProcess) {
|
||||||
}
|
executorService.fork {
|
||||||
} else {
|
val nodeInfo = Node(config.corda, MOCK_VERSION_INFO, initialiseSerialization = false).generateAndSaveNodeInfo()
|
||||||
// TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper
|
Pair(config, NotaryInfo(nodeInfo.legalIdentities[0], spec.validating))
|
||||||
// This causes two node info files to be generated.
|
}
|
||||||
startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map {
|
} else {
|
||||||
// Once done we have to read the signed node info file that's been generated
|
// TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper
|
||||||
val nodeInfoFile = config.corda.baseDirectory.list { paths ->
|
// This causes two node info files to be generated.
|
||||||
paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
|
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> {
|
private fun generateNodeNames(spec: NotarySpec): List<CordaX500Name> {
|
||||||
return (0 until spec.cluster!!.clusterSize).map { spec.name.copy(organisation = "${spec.name.organisation}-$it") }
|
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>>> {
|
private fun startNotaries(configs: List<NodeConfig>, localNetworkMap: LocalNetworkMap?, customOverrides: Map<String, Any?>): List<CordaFuture<List<NodeHandle>>> {
|
||||||
return notarySpecs.map {
|
return notarySpecs.zip(configs).map { (spec, config) ->
|
||||||
when (it.cluster) {
|
when (spec.cluster) {
|
||||||
null -> startSingleNotary(it, localNetworkMap, customOverrides)
|
null -> startSingleNotary(config, spec, localNetworkMap, customOverrides)
|
||||||
is ClusterSpec.Raft,
|
is ClusterSpec.Raft,
|
||||||
// DummyCluster is used for testing the notary communication path, and it does not matter
|
// 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
|
// 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")
|
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))
|
val notaryConfig = mapOf("notary" to mapOf("validating" to spec.validating))
|
||||||
return startRegisteredNode(
|
return startRegisteredNode(
|
||||||
spec.name,
|
config,
|
||||||
localNetworkMap,
|
localNetworkMap,
|
||||||
NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig + customOverrides, maximumHeapSize = spec.maximumHeapSize)
|
NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig + customOverrides, maximumHeapSize = spec.maximumHeapSize)
|
||||||
).map { listOf(it) }
|
).map { listOf(it) }
|
||||||
@ -585,20 +603,26 @@ class DriverDSLImpl(
|
|||||||
val nodeNames = generateNodeNames(spec)
|
val nodeNames = generateNodeNames(spec)
|
||||||
val clusterAddress = portAllocation.nextHostAndPort()
|
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
|
// Start the first node that will bootstrap the cluster
|
||||||
val firstNodeFuture = startRegisteredNode(
|
val firstNodeFuture = startRegisteredNode(
|
||||||
nodeNames[0],
|
firstConfig.getOrThrow(),
|
||||||
localNetworkMap,
|
localNetworkMap,
|
||||||
NodeParameters(rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, customOverrides = notaryConfig(clusterAddress))
|
firstParams
|
||||||
)
|
)
|
||||||
|
|
||||||
// All other nodes will join the cluster
|
// All other nodes will join the cluster
|
||||||
val restNodeFutures = nodeNames.drop(1).map {
|
val restNodeFutures = nodeNames.drop(1).map {
|
||||||
val nodeAddress = portAllocation.nextHostAndPort()
|
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(
|
startRegisteredNode(
|
||||||
it,
|
config.getOrThrow(),
|
||||||
localNetworkMap,
|
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 nodeFuture = if (parameters.startInSameProcess ?: startNodesInProcess) {
|
||||||
val nodeAndThreadFuture = startInProcessNode(executorService, config)
|
val nodeAndThreadFuture = startInProcessNode(executorService, config, allowHibernateToManageAppSchema)
|
||||||
shutdownManager.registerShutdown(
|
shutdownManager.registerShutdown(
|
||||||
nodeAndThreadFuture.map { (node, thread) ->
|
nodeAndThreadFuture.map { (node, thread) ->
|
||||||
{
|
{
|
||||||
@ -689,6 +713,9 @@ class DriverDSLImpl(
|
|||||||
nodeFuture
|
nodeFuture
|
||||||
} else {
|
} else {
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
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(
|
val process = startOutOfProcessNode(
|
||||||
config,
|
config,
|
||||||
quasarJarPath,
|
quasarJarPath,
|
||||||
@ -699,7 +726,10 @@ class DriverDSLImpl(
|
|||||||
parameters.maximumHeapSize,
|
parameters.maximumHeapSize,
|
||||||
parameters.logLevelOverride,
|
parameters.logLevelOverride,
|
||||||
identifier,
|
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
|
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
|
||||||
@ -853,7 +883,8 @@ class DriverDSLImpl(
|
|||||||
|
|
||||||
private fun startInProcessNode(
|
private fun startInProcessNode(
|
||||||
executorService: ScheduledExecutorService,
|
executorService: ScheduledExecutorService,
|
||||||
config: NodeConfig
|
config: NodeConfig,
|
||||||
|
allowHibernateToManageAppSchema: Boolean
|
||||||
): CordaFuture<Pair<NodeWithInfo, Thread>> {
|
): CordaFuture<Pair<NodeWithInfo, Thread>> {
|
||||||
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
|
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
|
||||||
return executorService.fork {
|
return executorService.fork {
|
||||||
@ -864,7 +895,7 @@ class DriverDSLImpl(
|
|||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
|
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
|
||||||
// TODO pass the version in?
|
// 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 nodeInfo = node.start()
|
||||||
val nodeWithInfo = NodeWithInfo(node, nodeInfo)
|
val nodeWithInfo = NodeWithInfo(node, nodeInfo)
|
||||||
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
||||||
@ -1241,7 +1272,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
|||||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||||
djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
|
djvmBootstrapSource = defaultParameters.djvmBootstrapSource,
|
||||||
djvmCordaSource = defaultParameters.djvmCordaSource,
|
djvmCordaSource = defaultParameters.djvmCordaSource,
|
||||||
environmentVariables = defaultParameters.environmentVariables
|
environmentVariables = defaultParameters.environmentVariables,
|
||||||
|
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||||
@ -1339,29 +1371,31 @@ fun <A> internalDriver(
|
|||||||
djvmBootstrapSource: Path? = null,
|
djvmBootstrapSource: Path? = null,
|
||||||
djvmCordaSource: List<Path> = emptyList(),
|
djvmCordaSource: List<Path> = emptyList(),
|
||||||
environmentVariables: Map<String, String> = emptyMap(),
|
environmentVariables: Map<String, String> = emptyMap(),
|
||||||
|
allowHibernateToManageAppSchema: Boolean = true,
|
||||||
dsl: DriverDSLImpl.() -> A
|
dsl: DriverDSLImpl.() -> A
|
||||||
): A {
|
): A {
|
||||||
return genericDriver(
|
return genericDriver(
|
||||||
driverDsl = DriverDSLImpl(
|
driverDsl = DriverDSLImpl(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
debugPortAllocation = debugPortAllocation,
|
debugPortAllocation = debugPortAllocation,
|
||||||
systemProperties = systemProperties,
|
systemProperties = systemProperties,
|
||||||
driverDirectory = driverDirectory.toAbsolutePath(),
|
driverDirectory = driverDirectory.toAbsolutePath(),
|
||||||
useTestClock = useTestClock,
|
useTestClock = useTestClock,
|
||||||
isDebug = isDebug,
|
isDebug = isDebug,
|
||||||
startNodesInProcess = startNodesInProcess,
|
startNodesInProcess = startNodesInProcess,
|
||||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
notarySpecs = notarySpecs,
|
notarySpecs = notarySpecs,
|
||||||
jmxPolicy = jmxPolicy,
|
jmxPolicy = jmxPolicy,
|
||||||
compatibilityZone = compatibilityZone,
|
compatibilityZone = compatibilityZone,
|
||||||
networkParameters = networkParameters,
|
networkParameters = networkParameters,
|
||||||
notaryCustomOverrides = notaryCustomOverrides,
|
notaryCustomOverrides = notaryCustomOverrides,
|
||||||
inMemoryDB = inMemoryDB,
|
inMemoryDB = inMemoryDB,
|
||||||
cordappsForAllNodes = cordappsForAllNodes,
|
cordappsForAllNodes = cordappsForAllNodes,
|
||||||
djvmBootstrapSource = djvmBootstrapSource,
|
djvmBootstrapSource = djvmBootstrapSource,
|
||||||
djvmCordaSource = djvmCordaSource,
|
djvmCordaSource = djvmCordaSource,
|
||||||
environmentVariables = environmentVariables
|
environmentVariables = environmentVariables,
|
||||||
|
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
dsl = dsl
|
dsl = dsl
|
||||||
|
@ -286,7 +286,8 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
|||||||
args.version,
|
args.version,
|
||||||
mockFlowManager,
|
mockFlowManager,
|
||||||
args.network.getServerThread(args.id),
|
args.network.getServerThread(args.id),
|
||||||
args.network.busyLatch
|
args.network.busyLatch,
|
||||||
|
allowHibernateToManageAppSchema = true
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val staticLog = contextLogger()
|
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 mockNet = args.network
|
||||||
val id = args.id
|
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 {
|
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.")
|
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()
|
return super.start()
|
||||||
|
@ -163,6 +163,7 @@ fun RPCSecurityManagerImpl.Companion.fromUserList(id: AuthServiceId, users: List
|
|||||||
/**
|
/**
|
||||||
* Convenience method for configuring a database for some tests.
|
* Convenience method for configuring a database for some tests.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("LongParameterList")
|
||||||
fun configureDatabase(hikariProperties: Properties,
|
fun configureDatabase(hikariProperties: Properties,
|
||||||
databaseConfig: DatabaseConfig,
|
databaseConfig: DatabaseConfig,
|
||||||
wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
|
wellKnownPartyFromX500Name: (CordaX500Name) -> Party?,
|
||||||
@ -170,9 +171,19 @@ fun configureDatabase(hikariProperties: Properties,
|
|||||||
schemaService: SchemaService = NodeSchemaService(),
|
schemaService: SchemaService = NodeSchemaService(),
|
||||||
internalSchemas: Set<MappedSchema> = NodeSchemaService().internalSchemas(),
|
internalSchemas: Set<MappedSchema> = NodeSchemaService().internalSchemas(),
|
||||||
cacheFactory: NamedCacheFactory = TestingNamedCacheFactory(),
|
cacheFactory: NamedCacheFactory = TestingNamedCacheFactory(),
|
||||||
ourName: CordaX500Name = TestIdentity(ALICE_NAME, 70).name): CordaPersistence {
|
ourName: CordaX500Name = TestIdentity(ALICE_NAME, 70).name,
|
||||||
val persistence = createCordaPersistence(databaseConfig, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous, schemaService, hikariProperties, cacheFactory, null)
|
runMigrationScripts: Boolean = true,
|
||||||
persistence.startHikariPool(hikariProperties, databaseConfig, internalSchemas, ourName = ourName)
|
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
|
return persistence
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user