mirror of
https://github.com/corda/corda.git
synced 2025-02-22 18:12:53 +00:00
Merge branch 'release/os/4.9' into shams-4.10-merge-c8135331
This commit is contained in:
commit
46914a2b35
@ -272,7 +272,7 @@ class HibernateAttachmentQueryCriteriaParser<T,R>(override val criteriaBuilder:
|
|||||||
class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractState>,
|
class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractState>,
|
||||||
val contractStateTypeMappings: Map<String, Set<String>>,
|
val contractStateTypeMappings: Map<String, Set<String>>,
|
||||||
override val criteriaBuilder: CriteriaBuilder,
|
override val criteriaBuilder: CriteriaBuilder,
|
||||||
val criteriaQuery: CriteriaQuery<Tuple>,
|
val criteriaQuery: CriteriaQuery<*>,
|
||||||
val vaultStates: Root<VaultSchemaV1.VaultStates>) : AbstractQueryCriteriaParser<QueryCriteria, IQueryCriteriaParser, Sort>(), IQueryCriteriaParser {
|
val vaultStates: Root<VaultSchemaV1.VaultStates>) : AbstractQueryCriteriaParser<QueryCriteria, IQueryCriteriaParser, Sort>(), IQueryCriteriaParser {
|
||||||
private companion object {
|
private companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
|
@ -35,7 +35,6 @@ import net.corda.core.node.services.vault.PageSpecification
|
|||||||
import net.corda.core.node.services.vault.QueryCriteria
|
import net.corda.core.node.services.vault.QueryCriteria
|
||||||
import net.corda.core.node.services.vault.Sort
|
import net.corda.core.node.services.vault.Sort
|
||||||
import net.corda.core.node.services.vault.SortAttribute
|
import net.corda.core.node.services.vault.SortAttribute
|
||||||
import net.corda.core.node.services.vault.builder
|
|
||||||
import net.corda.core.observable.internal.OnResilientSubscribe
|
import net.corda.core.observable.internal.OnResilientSubscribe
|
||||||
import net.corda.core.schemas.PersistentStateRef
|
import net.corda.core.schemas.PersistentStateRef
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -69,17 +68,21 @@ import java.security.PublicKey
|
|||||||
import java.sql.SQLException
|
import java.sql.SQLException
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.Arrays
|
import java.util.*
|
||||||
import java.util.UUID
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.CopyOnWriteArraySet
|
import java.util.concurrent.CopyOnWriteArraySet
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import javax.persistence.PersistenceException
|
import javax.persistence.PersistenceException
|
||||||
import javax.persistence.Tuple
|
import javax.persistence.Tuple
|
||||||
import javax.persistence.criteria.CriteriaBuilder
|
import javax.persistence.criteria.CriteriaBuilder
|
||||||
|
import javax.persistence.criteria.CriteriaQuery
|
||||||
import javax.persistence.criteria.CriteriaUpdate
|
import javax.persistence.criteria.CriteriaUpdate
|
||||||
import javax.persistence.criteria.Predicate
|
import javax.persistence.criteria.Predicate
|
||||||
import javax.persistence.criteria.Root
|
import javax.persistence.criteria.Root
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.collections.LinkedHashSet
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The vault service handles storage, retrieval and querying of states.
|
* The vault service handles storage, retrieval and querying of states.
|
||||||
@ -706,10 +709,8 @@ class NodeVaultService(
|
|||||||
paging: PageSpecification,
|
paging: PageSpecification,
|
||||||
sorting: Sort,
|
sorting: Sort,
|
||||||
contractStateType: Class<out T>): Vault.Page<T> {
|
contractStateType: Class<out T>): Vault.Page<T> {
|
||||||
// calculate total results where a page specification has been defined
|
val (criteriaQuery, criteriaParser) = buildCriteriaQuery<Tuple>(criteria, contractStateType, sorting)
|
||||||
val totalStatesAvailable = if (paging.isDefault) -1 else queryTotalStateCount(criteria, contractStateType)
|
val query = getSession().createQuery(criteriaQuery)
|
||||||
|
|
||||||
val (query, stateTypes) = createQuery(criteria, contractStateType, sorting)
|
|
||||||
query.setResultWindow(paging)
|
query.setResultWindow(paging)
|
||||||
|
|
||||||
val statesMetadata: MutableList<Vault.StateMetadata> = mutableListOf()
|
val statesMetadata: MutableList<Vault.StateMetadata> = mutableListOf()
|
||||||
@ -732,7 +733,14 @@ class NodeVaultService(
|
|||||||
ArrayList()
|
ArrayList()
|
||||||
)
|
)
|
||||||
|
|
||||||
return Vault.Page(states, statesMetadata, totalStatesAvailable, stateTypes, otherResults)
|
val totalStatesAvailable = when {
|
||||||
|
paging.isDefault -> -1L
|
||||||
|
// If the first page isn't full then we know that's all the states that are available
|
||||||
|
paging.pageNumber == DEFAULT_PAGE_NUM && states.size < paging.pageSize -> states.size.toLong()
|
||||||
|
else -> queryTotalStateCount(criteria, contractStateType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Vault.Page(states, statesMetadata, totalStatesAvailable, criteriaParser.stateTypes, otherResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <R> Query<R>.resultStream(paging: PageSpecification): Stream<R> {
|
private fun <R> Query<R>.resultStream(paging: PageSpecification): Stream<R> {
|
||||||
@ -761,19 +769,17 @@ class NodeVaultService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : ContractState> queryTotalStateCount(baseCriteria: QueryCriteria, contractStateType: Class<out T>): Long {
|
private fun <T : ContractState> queryTotalStateCount(criteria: QueryCriteria, contractStateType: Class<out T>): Long {
|
||||||
val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() }
|
val (criteriaQuery, criteriaParser) = buildCriteriaQuery<Long>(criteria, contractStateType, null)
|
||||||
val countCriteria = QueryCriteria.VaultCustomQueryCriteria(count, Vault.StateStatus.ALL)
|
criteriaQuery.select(criteriaBuilder.countDistinct(criteriaParser.vaultStates))
|
||||||
val criteria = baseCriteria.and(countCriteria)
|
val query = getSession().createQuery(criteriaQuery)
|
||||||
val (query) = createQuery(criteria, contractStateType, null)
|
return query.singleResult
|
||||||
val results = query.resultList
|
|
||||||
return results.last().toArray().last() as Long
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : ContractState> createQuery(criteria: QueryCriteria,
|
private inline fun <reified T> buildCriteriaQuery(criteria: QueryCriteria,
|
||||||
contractStateType: Class<out T>,
|
contractStateType: Class<out ContractState>,
|
||||||
sorting: Sort?): Pair<Query<Tuple>, Vault.StateStatus> {
|
sorting: Sort?): Pair<CriteriaQuery<T>, HibernateQueryCriteriaParser> {
|
||||||
val criteriaQuery = criteriaBuilder.createQuery(Tuple::class.java)
|
val criteriaQuery = criteriaBuilder.createQuery(T::class.java)
|
||||||
val criteriaParser = HibernateQueryCriteriaParser(
|
val criteriaParser = HibernateQueryCriteriaParser(
|
||||||
contractStateType,
|
contractStateType,
|
||||||
contractStateTypeMappings,
|
contractStateTypeMappings,
|
||||||
@ -782,8 +788,7 @@ class NodeVaultService(
|
|||||||
criteriaQuery.from(VaultSchemaV1.VaultStates::class.java)
|
criteriaQuery.from(VaultSchemaV1.VaultStates::class.java)
|
||||||
)
|
)
|
||||||
criteriaParser.parse(criteria, sorting)
|
criteriaParser.parse(criteria, sorting)
|
||||||
val query = getSession().createQuery(criteriaQuery)
|
return Pair(criteriaQuery, criteriaParser)
|
||||||
return Pair(query, criteriaParser.stateTypes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,7 +22,7 @@ class VaultQueryExceptionsTests : VaultQueryParties by rule {
|
|||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
@JvmField
|
@JvmField
|
||||||
val rule = object : VaultQueryTestRule() {
|
val rule = object : VaultQueryTestRule(persistentServices = false) {
|
||||||
override val cordappPackages = listOf(
|
override val cordappPackages = listOf(
|
||||||
"net.corda.testing.contracts",
|
"net.corda.testing.contracts",
|
||||||
"net.corda.finance.contracts",
|
"net.corda.finance.contracts",
|
||||||
|
@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.mock
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.packageName
|
import net.corda.core.internal.packageName
|
||||||
@ -37,6 +38,7 @@ import net.corda.testing.internal.configureDatabase
|
|||||||
import net.corda.testing.internal.vault.*
|
import net.corda.testing.internal.vault.*
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
||||||
|
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndPersistentServices
|
||||||
import net.corda.testing.node.makeTestIdentityService
|
import net.corda.testing.node.makeTestIdentityService
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatCode
|
import org.assertj.core.api.Assertions.assertThatCode
|
||||||
@ -102,7 +104,7 @@ interface VaultQueryParties {
|
|||||||
val cordappPackages: List<String>
|
val cordappPackages: List<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
open class VaultQueryTestRule : ExternalResource(), VaultQueryParties {
|
open class VaultQueryTestRule(private val persistentServices: Boolean) : ExternalResource(), VaultQueryParties {
|
||||||
override val alice = TestIdentity(ALICE_NAME, 70)
|
override val alice = TestIdentity(ALICE_NAME, 70)
|
||||||
override val bankOfCorda = TestIdentity(BOC_NAME)
|
override val bankOfCorda = TestIdentity(BOC_NAME)
|
||||||
override val bigCorp = TestIdentity(CordaX500Name("BigCorporation", "New York", "US"))
|
override val bigCorp = TestIdentity(CordaX500Name("BigCorporation", "New York", "US"))
|
||||||
@ -135,12 +137,22 @@ open class VaultQueryTestRule : ExternalResource(), VaultQueryParties {
|
|||||||
|
|
||||||
|
|
||||||
override fun before() {
|
override fun before() {
|
||||||
// register additional identities
|
val databaseAndServices = if (persistentServices) {
|
||||||
val databaseAndServices = makeTestDatabaseAndMockServices(
|
makeTestDatabaseAndPersistentServices(
|
||||||
cordappPackages,
|
cordappPackages,
|
||||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
megaCorp,
|
||||||
megaCorp,
|
moreKeys = setOf(DUMMY_NOTARY_KEY),
|
||||||
moreKeys = *arrayOf(DUMMY_NOTARY_KEY))
|
moreIdentities = setOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
|
makeTestDatabaseAndMockServices(
|
||||||
|
cordappPackages,
|
||||||
|
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
||||||
|
megaCorp,
|
||||||
|
moreKeys = *arrayOf(DUMMY_NOTARY_KEY)
|
||||||
|
)
|
||||||
|
}
|
||||||
database = databaseAndServices.first
|
database = databaseAndServices.first
|
||||||
services = databaseAndServices.second
|
services = databaseAndServices.second
|
||||||
vaultFiller = VaultFiller(services, dummyNotary)
|
vaultFiller = VaultFiller(services, dummyNotary)
|
||||||
@ -2832,9 +2844,8 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate {
|
class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val delegate = VaultQueryTestRule()
|
val delegate = VaultQueryTestRule(persistentServices = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@ -3137,4 +3148,34 @@ class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PersistentServicesVaultQueryTests : VaultQueryParties by delegate {
|
||||||
|
companion object {
|
||||||
|
val delegate = VaultQueryTestRule(persistentServices = true)
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val vaultQueryTestRule = delegate
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun `query on externalId which maps to multiple keys`() {
|
||||||
|
val externalId = UUID.randomUUID()
|
||||||
|
val page = database.transaction {
|
||||||
|
val keys = Array(2) { services.keyManagementService.freshKey(externalId) }
|
||||||
|
vaultFiller.fillWithDummyState(participants = keys.map(::AnonymousParty))
|
||||||
|
services.vaultService.queryBy<ContractState>(
|
||||||
|
VaultQueryCriteria(externalIds = listOf(externalId)),
|
||||||
|
paging = PageSpecification(DEFAULT_PAGE_NUM, 10)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assertThat(page.states).hasSize(1)
|
||||||
|
assertThat(page.totalStatesAvailable).isEqualTo(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user