CORDA-2291 enable Finance App 3.x on Corda 4.x - Liquibase script is not required(#4382)

Finance CorDapp v3.0 and core node 3.0 database tables doesn't have Liquibase migration scripts, now in Corda v4.0 the Liquibase has been introduced. Allow older  Finance Cordapp v3.0 which doesn't have Liquibase to run in node v4.0 and create Liquibase log entries for FinanceApp only if it has schema migration (so it’s of v4.0).

At implementation level: there is new case when database has already Liquibase control tables however it doesn’t contains entries related to tables created by FInnaceApp and if the FinaceApp has Liquibase scheme it means it needs to be added to Liquibase logs.
This commit is contained in:
szymonsztuka 2018-12-10 15:54:30 +00:00 committed by GitHub
parent f58757bda9
commit 7172048735
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -13,7 +13,7 @@ import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.io.Writer
import java.sql.Statement
import javax.sql.DataSource
class SchemaMigration(
@ -80,6 +80,8 @@ class SchemaMigration(
val resource = getMigrationResource(mappedSchema, classLoader)
when {
resource != null -> resource
// Corda OS FinanceApp in v3 has no Liquibase script, so no error is raised
(mappedSchema::class.qualifiedName == "net.corda.finance.schemas.CashSchemaV1" || mappedSchema::class.qualifiedName == "net.corda.finance.schemas.CommercialPaperSchemaV1") && mappedSchema.migrationResource == null -> null
else -> throw MissingMigrationException(mappedSchema)
}
}
@ -104,60 +106,75 @@ class SchemaMigration(
return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn)
}
/** For existing database created before verions 4.0 add Liquibase support - creates DATABASECHANGELOG and DATABASECHANGELOGLOCK tables and mark changesets are executed. */
/** For existing database created before verions 4.0 add Liquibase support - creates DATABASECHANGELOG and DATABASECHANGELOGLOCK tables and marks changesets as executed. */
private fun migrateOlderDatabaseToUseLiquibase(existingCheckpoints: Boolean): Boolean {
//workaround to detect that if Corda finance module is in use then the most recent version with Liquibase migration scripts was deployed
if (schemas.any { schema ->
(schema::class.qualifiedName == "net.corda.finance.schemas.CashSchemaV1" || schema::class.qualifiedName == "net.corda.finance.schemas.CommercialPaperSchemaV1")
&& schema.migrationResource == null
})
throw DatabaseMigrationException("Detected incompatible corda-finance cordapp without database migration scripts, replace the existing corda-finance-VERSION.jar with the latest one.")
val isExistingDBWithoutLiquibase = dataSource.connection.use {
(it.metaData.getTables(null, null, "NODE%", null).next() &&
!it.metaData.getTables(null, null, "DATABASECHANGELOG%", null).next())
val isFinanceAppWithLiquibase = schemas.any { schema ->
(schema::class.qualifiedName == "net.corda.finance.schemas.CashSchemaV1"
|| schema::class.qualifiedName == "net.corda.finance.schemas.CommercialPaperSchemaV1")
&& schema.migrationResource != null
}
val noLiquibaseEntryLogForFinanceApp: (Statement) -> Boolean = {
it.execute("SELECT COUNT(*) FROM DATABASECHANGELOG WHERE FILENAME IN ('migration/cash.changelog-init.xml','migration/commercial-paper.changelog-init.xml')")
if (it.resultSet.next())
it.resultSet.getInt(1) == 0
else
true
}
when {
isExistingDBWithoutLiquibase && existingCheckpoints -> throw CheckpointsException()
isExistingDBWithoutLiquibase -> {
// Virtual file name of the changelog that includes all schemas.
val dynamicInclude = "master.changelog.json"
dataSource.connection.use { connection ->
// Schema migrations pre release 4.0
val preV4Baseline = mutableListOf("migration/common.changelog-init.xml",
"migration/node-info.changelog-init.xml",
"migration/node-info.changelog-v1.xml",
"migration/node-info.changelog-v2.xml",
"migration/node-core.changelog-init.xml",
"migration/node-core.changelog-v3.xml",
"migration/node-core.changelog-v4.xml",
"migration/node-core.changelog-v5.xml",
"migration/node-core.changelog-pkey.xml",
"migration/vault-schema.changelog-init.xml",
"migration/vault-schema.changelog-v3.xml",
"migration/vault-schema.changelog-v4.xml",
"migration/vault-schema.changelog-pkey.xml")
val (isExistingDBWithoutLiquibase, isFinanceAppWithLiquibaseNotMigrated) = dataSource.connection.use {
if (schemas.any { schema -> schema.migrationResource == "cash.changelog-master" })
preV4Baseline.addAll(listOf("migration/cash.changelog-init.xml",
"migration/cash.changelog-v1.xml"))
val existingDatabase = it.metaData.getTables(null, null, "NODE%", null).next()
if (schemas.any { schema -> schema.migrationResource == "commercial-paper.changelog-master" })
preV4Baseline.addAll(listOf("migration/commercial-paper.changelog-init.xml",
"migration/commercial-paper.changelog-v1.xml"))
val hasLiquibase = it.metaData.getTables(null, null, "DATABASECHANGELOG%", null).next()
if (schemas.any { schema -> schema.migrationResource == "node-notary.changelog-master" })
preV4Baseline.addAll(listOf("migration/node-notary.changelog-init.xml",
"migration/node-notary.changelog-v1.xml"))
val isFinanceAppWithLiquibaseNotMigrated = isFinanceAppWithLiquibase // If Finance App is pre v4.0 then no need to migrate it so no need to check.
&& existingDatabase
&& (!hasLiquibase // Migrate as other tables.
|| (hasLiquibase && it.createStatement().use { noLiquibaseEntryLogForFinanceApp(it) })) // If Liquibase is already in the database check if Finance App schema log is missing.
val customResourceAccessor = CustomResourceAccessor(dynamicInclude, preV4Baseline, classLoader)
val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection)))
liquibase.changeLogSync(Contexts(), LabelExpression())
}
Pair(existingDatabase && !hasLiquibase, isFinanceAppWithLiquibaseNotMigrated)
}
if (isExistingDBWithoutLiquibase && existingCheckpoints)
throw CheckpointsException()
// Schema migrations pre release 4.0
val preV4Baseline = mutableListOf<String>()
if (isExistingDBWithoutLiquibase) {
preV4Baseline.addAll(listOf("migration/common.changelog-init.xml",
"migration/node-info.changelog-init.xml",
"migration/node-info.changelog-v1.xml",
"migration/node-info.changelog-v2.xml",
"migration/node-core.changelog-init.xml",
"migration/node-core.changelog-v3.xml",
"migration/node-core.changelog-v4.xml",
"migration/node-core.changelog-v5.xml",
"migration/node-core.changelog-pkey.xml",
"migration/vault-schema.changelog-init.xml",
"migration/vault-schema.changelog-v3.xml",
"migration/vault-schema.changelog-v4.xml",
"migration/vault-schema.changelog-pkey.xml"))
if (schemas.any { schema -> schema.migrationResource == "node-notary.changelog-master" })
preV4Baseline.addAll(listOf("migration/node-notary.changelog-init.xml",
"migration/node-notary.changelog-v1.xml"))
}
if (isFinanceAppWithLiquibaseNotMigrated) {
preV4Baseline.addAll(listOf("migration/cash.changelog-init.xml",
"migration/cash.changelog-v1.xml",
"migration/commercial-paper.changelog-init.xml",
"migration/commercial-paper.changelog-v1.xml"))
}
if (preV4Baseline.isNotEmpty()) {
val dynamicInclude = "master.changelog.json" // Virtual file name of the changelog that includes all schemas.
dataSource.connection.use { connection ->
val customResourceAccessor = CustomResourceAccessor(dynamicInclude, preV4Baseline, classLoader)
val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection)))
liquibase.changeLogSync(Contexts(), LabelExpression())
}
}
return isExistingDBWithoutLiquibase
return isExistingDBWithoutLiquibase || isFinanceAppWithLiquibaseNotMigrated
}
}