CORDA-3569 - Add RestrictedConnection and more blocked methods to RestrictedEntityManager (#6129)

* adding blocked functions ro RestrictedEntityManager and creating RestrictedConnection class

* adding flow tests and fixing issues regarding the review

* adding quasar util to gradle

* updating flow tests

* adding space before } at .isThrownBy()

* adding spaces
This commit is contained in:
nikinagy 2020-04-21 14:39:41 +01:00 committed by GitHub
parent 9ca251c65f
commit 2bcaa2ac80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 428 additions and 2 deletions

View File

@ -1,4 +1,5 @@
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.quasar-utils'
description 'NodeAPI tests that require node etc'

View File

@ -0,0 +1,78 @@
package net.corda.nodeapitests.internal.persistence
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.internal.persistence.RestrictedConnection
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
import org.assertj.core.api.Assertions
import org.junit.After
import org.junit.Before
import org.junit.Test
import kotlin.test.assertTrue
class RestrictedConnectionFlowTest {
private lateinit var aliceNode: StartedMockNode
private lateinit var mockNetwork: MockNetwork
@InitiatingFlow
class TestIfItIsRestrictedConnection : FlowLogic<Boolean>() {
@Suspendable
override fun call() : Boolean {
val connection = serviceHub.jdbcSession()
return connection is RestrictedConnection
}
}
@InitiatingFlow
class TestAutoCommitMethodIsBlocked : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val connection = serviceHub.jdbcSession()
connection.autoCommit = true
}
}
@InitiatingFlow
class TestCloseMethodIsBlocked : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val connection = serviceHub.jdbcSession()
connection.close()
}
}
@Before
fun init() {
mockNetwork = MockNetwork(MockNetworkParameters())
aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB"))
}
@After
fun done() {
mockNetwork.stopNodes()
}
@Test(timeout=300_000)
fun testIfItIsRestrictedConnection() {
assertTrue { aliceNode.startFlow(TestIfItIsRestrictedConnection()).get() }
mockNetwork.runNetwork()
}
@Test(timeout=300_000)
fun testMethodsAreBlocked() {
Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java)
.isThrownBy { aliceNode.startFlow(TestAutoCommitMethodIsBlocked()).getOrThrow() }
.withMessageContaining("This method cannot be called via ServiceHub.jdbcSession.")
Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java)
.isThrownBy { aliceNode.startFlow(TestCloseMethodIsBlocked()).getOrThrow() }
.withMessageContaining("This method cannot be called via ServiceHub.jdbcSession.")
mockNetwork.runNetwork()
}
}

View File

@ -0,0 +1,84 @@
package net.corda.nodeapitests.internal.persistence
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.internal.persistence.RestrictedEntityManager
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
import org.assertj.core.api.Assertions
import org.junit.After
import org.junit.Before
import org.junit.Test
import kotlin.test.assertTrue
class RestrictedEntityManagerFlowTest {
private lateinit var aliceNode: StartedMockNode
private lateinit var mockNetwork: MockNetwork
@InitiatingFlow
class TestIfItIsRestrictedEntityManager : FlowLogic<Boolean>() {
@Suspendable
override fun call() : Boolean {
var result = false
serviceHub.withEntityManager() {
result = this is RestrictedEntityManager
}
return result
}
}
@InitiatingFlow
class TestCloseMethodIsBlocked : FlowLogic<Unit>() {
@Suspendable
override fun call() {
serviceHub.withEntityManager() {
this.close()
}
}
}
@InitiatingFlow
class TestJoinTransactionMethodIsBlocked : FlowLogic<Unit>() {
@Suspendable
override fun call() {
serviceHub.withEntityManager() {
this.joinTransaction()
}
}
}
@Before
fun init() {
mockNetwork = MockNetwork(MockNetworkParameters())
aliceNode = mockNetwork.createPartyNode(CordaX500Name("Alice", "London", "GB"))
}
@After
fun done() {
mockNetwork.stopNodes()
}
@Test(timeout=300_000)
fun testIfItIsRestrictedConnection() {
assertTrue { aliceNode.startFlow(TestIfItIsRestrictedEntityManager()).get() }
mockNetwork.runNetwork()
}
@Test(timeout=300_000)
fun testMethodsAreBlocked() {
Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java)
.isThrownBy { aliceNode.startFlow(TestCloseMethodIsBlocked()).getOrThrow() }
.withMessageContaining("This method cannot be called via ServiceHub.withEntityManager.")
Assertions.assertThatExceptionOfType(UnsupportedOperationException::class.java)
.isThrownBy { aliceNode.startFlow(TestJoinTransactionMethodIsBlocked()).getOrThrow() }
.withMessageContaining("This method cannot be called via ServiceHub.withEntityManager.")
mockNetwork.runNetwork()
}
}

View File

@ -0,0 +1,80 @@
package net.corda.nodeapi.internal.persistence
import java.sql.Connection
import java.sql.Savepoint
import java.util.concurrent.Executor
/**
* A delegate of [Connection] which disallows some operations.
*/
@Suppress("TooManyFunctions")
class RestrictedConnection(private val delegate : Connection) : Connection by delegate {
override fun abort(executor: Executor?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun clearWarnings() {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun close() {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun commit() {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setSavepoint(): Savepoint? {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setSavepoint(name : String?): Savepoint? {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun releaseSavepoint(savepoint: Savepoint?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun rollback() {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun rollback(savepoint: Savepoint?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setCatalog(catalog : String?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setTransactionIsolation(level: Int) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setTypeMap(map: MutableMap<String, Class<*>>?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setHoldability(holdability: Int) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setSchema(schema: String?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setNetworkTimeout(executor: Executor?, milliseconds: Int) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setAutoCommit(autoCommit: Boolean) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
override fun setReadOnly(readOnly: Boolean) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.jdbcSession.")
}
}

View File

@ -1,6 +1,9 @@
package net.corda.nodeapi.internal.persistence
import javax.persistence.EntityManager
import javax.persistence.EntityTransaction
import javax.persistence.LockModeType
import javax.persistence.metamodel.Metamodel
/**
* A delegate of [EntityManager] which disallows some operations.
@ -15,5 +18,28 @@ class RestrictedEntityManager(private val delegate: EntityManager) : EntityManag
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
// TODO: Figure out which other methods on EntityManager need to be blocked?
override fun getMetamodel(): Metamodel? {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
override fun getTransaction(): EntityTransaction? {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
override fun joinTransaction() {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
override fun lock(entity: Any?, lockMode: LockModeType?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
override fun lock(entity: Any?, lockMode: LockModeType?, properties: MutableMap<String, Any>?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
override fun setProperty(propertyName: String?, value: Any?) {
throw UnsupportedOperationException("This method cannot be called via ServiceHub.withEntityManager.")
}
}

View File

@ -0,0 +1,104 @@
package net.corda.nodeapi.internal.persistence
import com.nhaarman.mockito_kotlin.mock
import org.junit.Test
import java.sql.Connection
import java.sql.Savepoint
class RestrictedConnectionTest {
private val connection : Connection = mock()
private val savePoint : Savepoint = mock()
private val restrictedConnection : RestrictedConnection = RestrictedConnection(connection)
companion object {
private const val TEST_STRING : String = "test"
private const val TEST_INT : Int = 1
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testAbort() {
restrictedConnection.abort { println("I'm just an executor for this test...") }
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testClearWarnings() {
restrictedConnection.clearWarnings()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testClose() {
restrictedConnection.close()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testCommit() {
restrictedConnection.commit()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetSavepoint() {
restrictedConnection.setSavepoint()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetSavepointWithName() {
restrictedConnection.setSavepoint(TEST_STRING)
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testReleaseSavepoint() {
restrictedConnection.releaseSavepoint(savePoint)
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testRollback() {
restrictedConnection.rollback()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testRollbackWithSavepoint() {
restrictedConnection.rollback(savePoint)
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetCatalog() {
restrictedConnection.catalog = TEST_STRING
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetTransactionIsolation() {
restrictedConnection.transactionIsolation = TEST_INT
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetTypeMap() {
val map: MutableMap<String, Class<*>> = mutableMapOf()
restrictedConnection.typeMap = map
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetHoldability() {
restrictedConnection.holdability = TEST_INT
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetSchema() {
restrictedConnection.schema = TEST_STRING
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetNetworkTimeout() {
restrictedConnection.setNetworkTimeout({ println("I'm just an executor for this test...") }, TEST_INT)
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetAutoCommit() {
restrictedConnection.autoCommit = true
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetReadOnly() {
restrictedConnection.isReadOnly = true
}
}

View File

@ -0,0 +1,52 @@
package net.corda.nodeapi.internal.persistence
import com.nhaarman.mockito_kotlin.mock
import org.junit.Test
import javax.persistence.EntityManager
import javax.persistence.LockModeType
class RestrtictedEntityManagerTest {
private val entitymanager = mock<EntityManager>()
private val restrictedEntityManager = RestrictedEntityManager(entitymanager)
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testClose() {
restrictedEntityManager.close()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testClear() {
restrictedEntityManager.clear()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testGetMetaModel() {
restrictedEntityManager.getMetamodel()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testGetTransaction() {
restrictedEntityManager.getTransaction()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testJoinTransaction() {
restrictedEntityManager.joinTransaction()
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testLockWithTwoParameters() {
restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC)
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testLockWithThreeParameters() {
val map: MutableMap<String,Any> = mutableMapOf()
restrictedEntityManager.lock(Object(), LockModeType.OPTIMISTIC,map)
}
@Test(expected = UnsupportedOperationException::class, timeout=300_000)
fun testSetProperty() {
restrictedEntityManager.setProperty("number", 12)
}
}

View File

@ -161,6 +161,7 @@ import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.internal.persistence.DatabaseIncompatibleException
import net.corda.nodeapi.internal.persistence.OutstandingDatabaseChangesException
import net.corda.nodeapi.internal.persistence.RestrictedConnection
import net.corda.nodeapi.internal.persistence.SchemaMigration
import net.corda.tools.shell.InteractiveShell
import org.apache.activemq.artemis.utils.ReusableLatch
@ -1185,7 +1186,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
return flowManager.getFlowFactoryForInitiatingFlow(initiatingFlowClass)
}
override fun jdbcSession(): Connection = database.createSession()
override fun jdbcSession(): Connection = RestrictedConnection(database.createSession())
override fun <T : Any?> withEntityManager(block: EntityManager.() -> T): T {
return database.transaction {