mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
Tighten up transaction creation code to flag situations where calling code SHOULD BE within a demarcated transaction boundary. (#869)
Now fail fast with an appropriate message indicating course of action to be taken.
This commit is contained in:
parent
d54f66ccb0
commit
8aa325d9f7
@ -131,7 +131,7 @@ class KotlinConfigurationTransactionWrapper(private val model: EntityModel,
|
|||||||
override fun getConnection(): Connection {
|
override fun getConnection(): Connection {
|
||||||
val tx = TransactionManager.manager.currentOrNull()
|
val tx = TransactionManager.manager.currentOrNull()
|
||||||
return CordaConnection(
|
return CordaConnection(
|
||||||
tx?.connection ?: TransactionManager.manager.newTransaction(Connection.TRANSACTION_REPEATABLE_READ).connection
|
tx?.connection ?: throw IllegalStateException("Was expecting to find database transaction: must wrap calling code within a transaction.")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class RequeryConfiguration(val properties: Properties, val useDefaultLogging: Bo
|
|||||||
// TODO: remove once Requery supports QUERY WITH COMPOSITE_KEY IN
|
// TODO: remove once Requery supports QUERY WITH COMPOSITE_KEY IN
|
||||||
fun jdbcSession(): Connection {
|
fun jdbcSession(): Connection {
|
||||||
val ctx = TransactionManager.manager.currentOrNull()
|
val ctx = TransactionManager.manager.currentOrNull()
|
||||||
return ctx?.connection ?: TransactionManager.manager.newTransaction(Connection.TRANSACTION_REPEATABLE_READ).connection
|
return ctx?.connection ?: throw IllegalStateException("Was expecting to find database transaction: must wrap calling code within a transaction.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ import net.corda.node.services.database.RequeryConfiguration
|
|||||||
import net.corda.node.services.persistence.schemas.AttachmentEntity
|
import net.corda.node.services.persistence.schemas.AttachmentEntity
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
|
import net.corda.node.utilities.transaction
|
||||||
import net.corda.testing.node.makeTestDataSourceProperties
|
import net.corda.testing.node.makeTestDataSourceProperties
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -55,7 +55,7 @@ class NodeAttachmentStorageTest {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
TransactionManager.current().close()
|
dataSource.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -63,76 +63,84 @@ class NodeAttachmentStorageTest {
|
|||||||
val testJar = makeTestJar()
|
val testJar = makeTestJar()
|
||||||
val expectedHash = testJar.readAll().sha256()
|
val expectedHash = testJar.readAll().sha256()
|
||||||
|
|
||||||
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
database.transaction {
|
||||||
val id = testJar.read { storage.importAttachment(it) }
|
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
||||||
assertEquals(expectedHash, id)
|
val id = testJar.read { storage.importAttachment(it) }
|
||||||
|
assertEquals(expectedHash, id)
|
||||||
|
|
||||||
assertNull(storage.openAttachment(SecureHash.randomSHA256()))
|
assertNull(storage.openAttachment(SecureHash.randomSHA256()))
|
||||||
val stream = storage.openAttachment(expectedHash)!!.openAsJAR()
|
val stream = storage.openAttachment(expectedHash)!!.openAsJAR()
|
||||||
val e1 = stream.nextJarEntry!!
|
val e1 = stream.nextJarEntry!!
|
||||||
assertEquals("test1.txt", e1.name)
|
assertEquals("test1.txt", e1.name)
|
||||||
assertEquals(stream.readBytes().toString(Charset.defaultCharset()), "This is some useful content")
|
assertEquals(stream.readBytes().toString(Charset.defaultCharset()), "This is some useful content")
|
||||||
val e2 = stream.nextJarEntry!!
|
val e2 = stream.nextJarEntry!!
|
||||||
assertEquals("test2.txt", e2.name)
|
assertEquals("test2.txt", e2.name)
|
||||||
assertEquals(stream.readBytes().toString(Charset.defaultCharset()), "Some more useful content")
|
assertEquals(stream.readBytes().toString(Charset.defaultCharset()), "Some more useful content")
|
||||||
|
|
||||||
stream.close()
|
stream.close()
|
||||||
|
|
||||||
storage.openAttachment(id)!!.openAsJAR().use {
|
storage.openAttachment(id)!!.openAsJAR().use {
|
||||||
it.nextJarEntry
|
it.nextJarEntry
|
||||||
it.readBytes()
|
it.readBytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `duplicates not allowed`() {
|
fun `duplicates not allowed`() {
|
||||||
val testJar = makeTestJar()
|
val testJar = makeTestJar()
|
||||||
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
database.transaction {
|
||||||
testJar.read {
|
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
||||||
storage.importAttachment(it)
|
|
||||||
}
|
|
||||||
assertFailsWith<FileAlreadyExistsException> {
|
|
||||||
testJar.read {
|
testJar.read {
|
||||||
storage.importAttachment(it)
|
storage.importAttachment(it)
|
||||||
}
|
}
|
||||||
|
assertFailsWith<FileAlreadyExistsException> {
|
||||||
|
testJar.read {
|
||||||
|
storage.importAttachment(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `corrupt entry throws exception`() {
|
fun `corrupt entry throws exception`() {
|
||||||
val testJar = makeTestJar()
|
val testJar = makeTestJar()
|
||||||
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
database.transaction {
|
||||||
val id = testJar.read { storage.importAttachment(it) }
|
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
||||||
|
val id = testJar.read { storage.importAttachment(it) }
|
||||||
|
|
||||||
// Corrupt the file in the store.
|
// Corrupt the file in the store.
|
||||||
val bytes = testJar.readAll()
|
val bytes = testJar.readAll()
|
||||||
val corruptBytes = "arggghhhh".toByteArray()
|
val corruptBytes = "arggghhhh".toByteArray()
|
||||||
System.arraycopy(corruptBytes, 0, bytes, 0, corruptBytes.size)
|
System.arraycopy(corruptBytes, 0, bytes, 0, corruptBytes.size)
|
||||||
val corruptAttachment = AttachmentEntity()
|
val corruptAttachment = AttachmentEntity()
|
||||||
corruptAttachment.attId = id
|
corruptAttachment.attId = id
|
||||||
corruptAttachment.content = bytes
|
corruptAttachment.content = bytes
|
||||||
storage.session.update(corruptAttachment)
|
storage.session.update(corruptAttachment)
|
||||||
|
|
||||||
val e = assertFailsWith<NodeAttachmentService.HashMismatchException> {
|
val e = assertFailsWith<NodeAttachmentService.HashMismatchException> {
|
||||||
storage.openAttachment(id)!!.open().use { it.readBytes() }
|
storage.openAttachment(id)!!.open().use { it.readBytes() }
|
||||||
}
|
}
|
||||||
assertEquals(e.expected, id)
|
assertEquals(e.expected, id)
|
||||||
|
|
||||||
// But if we skip around and read a single entry, no exception is thrown.
|
// But if we skip around and read a single entry, no exception is thrown.
|
||||||
storage.openAttachment(id)!!.openAsJAR().use {
|
storage.openAttachment(id)!!.openAsJAR().use {
|
||||||
it.nextJarEntry
|
it.nextJarEntry
|
||||||
it.readBytes()
|
it.readBytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `non jar rejected`() {
|
fun `non jar rejected`() {
|
||||||
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
database.transaction {
|
||||||
val path = fs.getPath("notajar")
|
val storage = NodeAttachmentService(fs.getPath("/"), dataSourceProperties, MetricRegistry())
|
||||||
path.writeLines(listOf("Hey", "there!"))
|
val path = fs.getPath("notajar")
|
||||||
path.read {
|
path.writeLines(listOf("Hey", "there!"))
|
||||||
assertFailsWith<IllegalArgumentException>("either empty or not a JAR") {
|
path.read {
|
||||||
storage.importAttachment(it)
|
assertFailsWith<IllegalArgumentException>("either empty or not a JAR") {
|
||||||
|
storage.importAttachment(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user