[CORDA-1596] Improve error message when CorDapp schema changes (#4506)

* Raise schema exceptions with better error

* Add test and use new exceptions
This commit is contained in:
JamesHR3 2019-01-29 08:17:05 +00:00 committed by GitHub
parent 6b0b8b394c
commit 22462cc1e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 1 deletions

View File

@ -4,6 +4,7 @@ import co.paralleluniverse.strands.Strand
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger
import org.hibernate.tool.schema.spi.SchemaManagementException
import rx.Observable
import rx.Subscriber
import rx.subjects.UnicastSubject
@ -101,7 +102,14 @@ class CordaPersistence(
private val defaultIsolationLevel = databaseConfig.transactionIsolationLevel
val hibernateConfig: HibernateConfiguration by lazy {
transaction {
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader)
try {
HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader)
} catch (e: Exception) {
when (e) {
is SchemaManagementException -> throw HibernateSchemaChangeException("Incompatible schema change detected. Please run the node with database.initialiseSchema=true. Reason: ${e.message}", e)
else -> throw HibernateConfigException("Could not create Hibernate configuration: ${e.message}", e)
}
}
}
}
@ -339,3 +347,7 @@ private fun Throwable.hasSQLExceptionCause(): Boolean =
}
class CouldNotCreateDataSourceException(override val message: String?, override val cause: Throwable? = null) : Exception()
class HibernateSchemaChangeException(override val message: String?, override val cause: Throwable? = null): Exception()
class HibernateConfigException(override val message: String?, override val cause: Throwable? = null): Exception()

View File

@ -39,6 +39,7 @@ import net.corda.node.services.vault.VaultSchemaV1
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
import net.corda.nodeapi.internal.persistence.HibernateSchemaChangeException
import net.corda.testing.core.*
import net.corda.testing.internal.configureDatabase
import net.corda.testing.internal.rigorousMock
@ -50,6 +51,7 @@ import net.corda.testing.node.MockServices
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.hibernate.SessionFactory
import org.junit.*
import java.math.BigDecimal
@ -963,4 +965,34 @@ class HibernateConfigurationTest {
private fun toStateRef(pStateRef: PersistentStateRef): StateRef {
return StateRef(SecureHash.parse(pStateRef.txId), pStateRef.index)
}
@Test
fun `schema change`() {
fun createNewDB(schemas: Set<MappedSchema>, initialiseSchema: Boolean = true): CordaPersistence {
val schemaService = NodeSchemaService(extraSchemas = schemas)
val dataSourceProps = makeTestDataSourceProperties("aa")
val identityService = mock<IdentityService>().also { mock ->
doReturn(null).whenever(mock).wellKnownPartyFromAnonymous(any<AbstractParty>())
listOf(dummyCashIssuer, dummyNotary).forEach {
doReturn(it.party).whenever(mock).wellKnownPartyFromAnonymous(it.party)
doReturn(it.party).whenever(mock).wellKnownPartyFromX500Name(it.name)
}
}
database = configureDatabase(dataSourceProps, DatabaseConfig(initialiseSchema = initialiseSchema), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService)
return database
}
createNewDB(setOf(CashSchemaV1, SampleCashSchemaV1)).apply {
database.transaction {
vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 4, issuer.ref(1), rng = Random(0L))
}
}
createNewDB(setOf(CashSchemaV1, SampleCashSchemaV1, SampleCashSchemaV2), initialiseSchema = false).use {
assertThatThrownBy {
it.transaction {
vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 4, issuer.ref(1), rng = Random(0L))
}
}.isInstanceOf(HibernateSchemaChangeException::class.java)
}
}
}