Revert "OS 4.3 to OS 4.4 merge on 2019/09/30 (#5534)"

This reverts commit 191fb83dec283259fb2f186c2df5a88659b0e3ac.
This commit is contained in:
Stefano Franz 2019-10-01 16:37:49 +01:00 committed by GitHub
parent 191fb83dec
commit fa2720783a
23 changed files with 232 additions and 584 deletions

View File

@ -1,8 +1,8 @@
package net.corda.client.jfx.model package net.corda.client.jfx.model
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.CordaRPCConnection import net.corda.client.rpc.internal.ReconnectingCordaRPCOps
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -48,7 +48,7 @@ class NodeMonitorModel : AutoCloseable {
val progressTracking: Observable<ProgressTrackingEvent> = progressTrackingSubject val progressTracking: Observable<ProgressTrackingEvent> = progressTrackingSubject
val networkMap: Observable<MapChange> = networkMapSubject val networkMap: Observable<MapChange> = networkMapSubject
private lateinit var rpc: CordaRPCConnection private lateinit var rpc: CordaRPCOps
val proxyObservable = SimpleObjectProperty<CordaRPCOps>() val proxyObservable = SimpleObjectProperty<CordaRPCOps>()
lateinit var notaryIdentities: List<Party> lateinit var notaryIdentities: List<Party>
@ -61,7 +61,7 @@ class NodeMonitorModel : AutoCloseable {
*/ */
override fun close() { override fun close() {
try { try {
rpc.close() (rpc as ReconnectingCordaRPCOps).close()
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Error closing RPC connection to node", e) logger.error("Error closing RPC connection to node", e)
} }
@ -72,42 +72,35 @@ class NodeMonitorModel : AutoCloseable {
* TODO provide an unsubscribe mechanism * TODO provide an unsubscribe mechanism
*/ */
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) { fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
rpc = CordaRPCClient(nodeHostAndPort).start(username, password) rpc = ReconnectingCordaRPCOps(nodeHostAndPort, username, password, CordaRPCClientConfiguration.DEFAULT)
proxyObservable.value = rpc.proxy
proxyObservable.value = rpc
// Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates // Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates
val ( val (statesSnapshot, vaultUpdates) = rpc.vaultTrackBy<ContractState>(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL),
statesSnapshot, PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
vaultUpdates val unconsumedStates = statesSnapshot.states.filterIndexed { index, _ ->
) = rpc.proxy.vaultTrackBy<ContractState>( statesSnapshot.statesMetadata[index].status == Vault.StateStatus.UNCONSUMED
QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL),
PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE)
)
val unconsumedStates =
statesSnapshot.states.filterIndexed { index, _ ->
statesSnapshot.statesMetadata[index].status == Vault.StateStatus.UNCONSUMED
}.toSet() }.toSet()
val consumedStates = statesSnapshot.states.toSet() - unconsumedStates val consumedStates = statesSnapshot.states.toSet() - unconsumedStates
val initialVaultUpdate = Vault.Update(consumedStates, unconsumedStates, references = emptySet()) val initialVaultUpdate = Vault.Update(consumedStates, unconsumedStates, references = emptySet())
vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject::onNext) vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject::onNext)
// Transactions // Transactions
val (transactions, newTransactions) = val (transactions, newTransactions) = @Suppress("DEPRECATION") rpc.internalVerifiedTransactionsFeed()
@Suppress("DEPRECATION") rpc.proxy.internalVerifiedTransactionsFeed()
newTransactions.startWith(transactions).subscribe(transactionsSubject::onNext) newTransactions.startWith(transactions).subscribe(transactionsSubject::onNext)
// SM -> TX mapping // SM -> TX mapping
val (smTxMappings, futureSmTxMappings) = val (smTxMappings, futureSmTxMappings) = rpc.stateMachineRecordedTransactionMappingFeed()
rpc.proxy.stateMachineRecordedTransactionMappingFeed()
futureSmTxMappings.startWith(smTxMappings).subscribe(stateMachineTransactionMappingSubject::onNext) futureSmTxMappings.startWith(smTxMappings).subscribe(stateMachineTransactionMappingSubject::onNext)
// Parties on network // Parties on network
val (parties, futurePartyUpdate) = rpc.proxy.networkMapFeed() val (parties, futurePartyUpdate) = rpc.networkMapFeed()
futurePartyUpdate.startWith(parties.map(MapChange::Added)).subscribe(networkMapSubject::onNext) futurePartyUpdate.startWith(parties.map(MapChange::Added)).subscribe(networkMapSubject::onNext)
val stateMachines = rpc.proxy.stateMachinesSnapshot() val stateMachines = rpc.stateMachinesSnapshot()
notaryIdentities = rpc.proxy.notaryIdentities() notaryIdentities = rpc.notaryIdentities()
// Extract the flow tracking stream // Extract the flow tracking stream
// TODO is there a nicer way of doing this? Stream of streams in general results in code like this... // TODO is there a nicer way of doing this? Stream of streams in general results in code like this...

View File

@ -56,8 +56,8 @@ class CordaRPCClientReconnectionTest {
maxReconnectAttempts = 5 maxReconnectAttempts = 5
)) ))
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect).proxy as ReconnectingCordaRPCOps).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps val rpcOps = it
val networkParameters = rpcOps.networkParameters val networkParameters = rpcOps.networkParameters
val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java) val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java)
cashStatesFeed.updates.subscribe { latch.countDown() } cashStatesFeed.updates.subscribe { latch.countDown() }
@ -96,8 +96,8 @@ class CordaRPCClientReconnectionTest {
maxReconnectAttempts = 5 maxReconnectAttempts = 5
)) ))
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect).proxy as ReconnectingCordaRPCOps).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps val rpcOps = it
val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java) val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java)
val subscription = cashStatesFeed.updates.subscribe { latch.countDown() } val subscription = cashStatesFeed.updates.subscribe { latch.countDown() }
rpcOps.startTrackedFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue.get() rpcOps.startTrackedFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue.get()
@ -113,7 +113,6 @@ class CordaRPCClientReconnectionTest {
latch.await(4, TimeUnit.SECONDS) latch.await(4, TimeUnit.SECONDS)
} }
} }
} }
} }
@ -137,8 +136,8 @@ class CordaRPCClientReconnectionTest {
maxReconnectAttempts = 5 maxReconnectAttempts = 5
)) ))
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect).proxy as ReconnectingCordaRPCOps).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps val rpcOps = it
val networkParameters = rpcOps.networkParameters val networkParameters = rpcOps.networkParameters
val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java) val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java)
cashStatesFeed.updates.subscribe { latch.countDown() } cashStatesFeed.updates.subscribe { latch.countDown() }

View File

@ -27,9 +27,6 @@ import net.corda.serialization.internal.amqp.SerializationFactoryCacheKey
import net.corda.serialization.internal.amqp.SerializerFactory import net.corda.serialization.internal.amqp.SerializerFactory
import java.time.Duration import java.time.Duration
import java.util.* import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
/** /**
* This class is essentially just a wrapper for an RPCConnection<CordaRPCOps> and can be treated identically. * This class is essentially just a wrapper for an RPCConnection<CordaRPCOps> and can be treated identically.
@ -38,10 +35,9 @@ import java.util.concurrent.TimeUnit
*/ */
class CordaRPCConnection private constructor( class CordaRPCConnection private constructor(
private val oneTimeConnection: RPCConnection<CordaRPCOps>?, private val oneTimeConnection: RPCConnection<CordaRPCOps>?,
private val observersPool: ExecutorService?,
private val reconnectingCordaRPCOps: ReconnectingCordaRPCOps? private val reconnectingCordaRPCOps: ReconnectingCordaRPCOps?
) : RPCConnection<CordaRPCOps> { ) : RPCConnection<CordaRPCOps> {
internal constructor(connection: RPCConnection<CordaRPCOps>?) : this(connection, null, null) internal constructor(connection: RPCConnection<CordaRPCOps>?) : this(connection, null)
companion object { companion object {
@CordaInternal @CordaInternal
@ -50,20 +46,14 @@ class CordaRPCConnection private constructor(
password: String, password: String,
addresses: List<NetworkHostAndPort>, addresses: List<NetworkHostAndPort>,
rpcConfiguration: CordaRPCClientConfiguration, rpcConfiguration: CordaRPCClientConfiguration,
gracefulReconnect: GracefulReconnect, gracefulReconnect: GracefulReconnect
sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null
): CordaRPCConnection { ): CordaRPCConnection {
val observersPool: ExecutorService = Executors.newCachedThreadPool() return CordaRPCConnection(null, ReconnectingCordaRPCOps(
return CordaRPCConnection(null, observersPool, ReconnectingCordaRPCOps(
addresses, addresses,
username, username,
password, password,
rpcConfiguration, rpcConfiguration,
gracefulReconnect, gracefulReconnect
sslConfiguration,
classLoader,
observersPool
)) ))
} }
} }
@ -79,18 +69,7 @@ class CordaRPCConnection private constructor(
override fun forceClose() = actualConnection.forceClose() override fun forceClose() = actualConnection.forceClose()
override fun close() { override fun close() = actualConnection.close()
try {
actualConnection.close()
} finally {
observersPool?.apply {
shutdown()
if (!awaitTermination(@Suppress("MagicNumber")30, TimeUnit.SECONDS)) {
shutdownNow()
}
}
}
}
} }
/** /**
@ -343,36 +322,19 @@ class CordaRPCClient private constructor(
) { ) {
@JvmOverloads @JvmOverloads
constructor( constructor(hostAndPort: NetworkHostAndPort, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT) : this(
hostAndPort: NetworkHostAndPort, hostAndPort = hostAndPort, haAddressPool = emptyList(), configuration = configuration
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT
) : this(
hostAndPort = hostAndPort,
haAddressPool = emptyList(),
configuration = configuration
) )
constructor( constructor(hostAndPort: NetworkHostAndPort,
hostAndPort: NetworkHostAndPort, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, classLoader: ClassLoader): this(hostAndPort, configuration, null, classLoader = classLoader)
classLoader: ClassLoader
): this(
hostAndPort,
configuration,
null,
classLoader = classLoader
)
constructor( constructor(
hostAndPort: NetworkHostAndPort, hostAndPort: NetworkHostAndPort,
sslConfiguration: ClientRpcSslOptions? = null, sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null classLoader: ClassLoader? = null
) : this( ) : this(hostAndPort = hostAndPort, haAddressPool = emptyList(), sslConfiguration = sslConfiguration, classLoader = classLoader)
hostAndPort = hostAndPort,
haAddressPool = emptyList(),
sslConfiguration = sslConfiguration,
classLoader = classLoader
)
@JvmOverloads @JvmOverloads
constructor( constructor(
@ -380,13 +342,7 @@ class CordaRPCClient private constructor(
configuration: CordaRPCClientConfiguration, configuration: CordaRPCClientConfiguration,
sslConfiguration: ClientRpcSslOptions?, sslConfiguration: ClientRpcSslOptions?,
classLoader: ClassLoader? = null classLoader: ClassLoader? = null
) : this( ) : this(hostAndPort = hostAndPort, haAddressPool = emptyList(), configuration = configuration, sslConfiguration = sslConfiguration, classLoader = classLoader)
hostAndPort = hostAndPort,
haAddressPool = emptyList(),
configuration = configuration,
sslConfiguration = sslConfiguration,
classLoader = classLoader
)
@JvmOverloads @JvmOverloads
constructor( constructor(
@ -394,13 +350,7 @@ class CordaRPCClient private constructor(
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT, configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
sslConfiguration: ClientRpcSslOptions? = null, sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null classLoader: ClassLoader? = null
) : this( ) : this(hostAndPort = null, haAddressPool = haAddressPool, configuration = configuration, sslConfiguration = sslConfiguration, classLoader = classLoader)
hostAndPort = null,
haAddressPool = haAddressPool,
configuration = configuration,
sslConfiguration = sslConfiguration,
classLoader = classLoader
)
// Here to keep the keep ABI compatibility happy // Here to keep the keep ABI compatibility happy
companion object {} companion object {}
@ -412,23 +362,11 @@ class CordaRPCClient private constructor(
try { try {
val cache = Caffeine.newBuilder().maximumSize(128).build<SerializationFactoryCacheKey, SerializerFactory>().asMap() val cache = Caffeine.newBuilder().maximumSize(128).build<SerializationFactoryCacheKey, SerializerFactory>().asMap()
// If the client has explicitly provided a classloader use this one to scan for custom serializers, // If the client has explicitly provided a classloader use this one to scan for custom serializers, otherwise use the current one.
// otherwise use the current one.
val serializationClassLoader = this.classLoader ?: this.javaClass.classLoader val serializationClassLoader = this.classLoader ?: this.javaClass.classLoader
val customSerializers = createInstancesOfClassesImplementing( val customSerializers = createInstancesOfClassesImplementing(serializationClassLoader, SerializationCustomSerializer::class.java)
serializationClassLoader, val serializationWhitelists = ServiceLoader.load(SerializationWhitelist::class.java, serializationClassLoader).toSet()
SerializationCustomSerializer::class.java AMQPClientSerializationScheme.initialiseSerialization(serializationClassLoader, customSerializers, serializationWhitelists, cache)
)
val serializationWhitelists = ServiceLoader.load(
SerializationWhitelist::class.java,
serializationClassLoader
).toSet()
AMQPClientSerializationScheme.initialiseSerialization(
serializationClassLoader,
customSerializers,
serializationWhitelists,
cache
)
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
// Race e.g. two of these constructed in parallel, ignore. // Race e.g. two of these constructed in parallel, ignore.
} }
@ -463,11 +401,7 @@ class CordaRPCClient private constructor(
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout. * @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
*/ */
@JvmOverloads @JvmOverloads
fun start( fun start(username: String, password: String, gracefulReconnect: GracefulReconnect? = null): CordaRPCConnection {
username: String,
password: String,
gracefulReconnect: GracefulReconnect? = null
): CordaRPCConnection {
return start(username, password, null, null, gracefulReconnect) return start(username, password, null, null, gracefulReconnect)
} }
@ -484,12 +418,7 @@ class CordaRPCClient private constructor(
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout. * @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
*/ */
@JvmOverloads @JvmOverloads
fun start( fun start(username: String, password: String, targetLegalIdentity: CordaX500Name, gracefulReconnect: GracefulReconnect? = null): CordaRPCConnection {
username: String,
password: String,
targetLegalIdentity: CordaX500Name,
gracefulReconnect: GracefulReconnect? = null
): CordaRPCConnection {
return start(username, password, null, null, targetLegalIdentity, gracefulReconnect) return start(username, password, null, null, targetLegalIdentity, gracefulReconnect)
} }
@ -507,13 +436,7 @@ class CordaRPCClient private constructor(
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout. * @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
*/ */
@JvmOverloads @JvmOverloads
fun start( fun start(username: String, password: String, externalTrace: Trace?, impersonatedActor: Actor?, gracefulReconnect: GracefulReconnect? = null): CordaRPCConnection {
username: String,
password: String,
externalTrace: Trace?,
impersonatedActor: Actor?,
gracefulReconnect: GracefulReconnect? = null
): CordaRPCConnection {
return start(username, password, externalTrace, impersonatedActor, null, gracefulReconnect) return start(username, password, externalTrace, impersonatedActor, null, gracefulReconnect)
} }
@ -534,14 +457,7 @@ class CordaRPCClient private constructor(
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout. * @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
*/ */
@JvmOverloads @JvmOverloads
fun start( fun start(username: String, password: String, externalTrace: Trace?, impersonatedActor: Actor?, targetLegalIdentity: CordaX500Name?, gracefulReconnect: GracefulReconnect? = null): CordaRPCConnection {
username: String,
password: String,
externalTrace: Trace?,
impersonatedActor: Actor?,
targetLegalIdentity: CordaX500Name?,
gracefulReconnect: GracefulReconnect? = null
): CordaRPCConnection {
val addresses = if (haAddressPool.isEmpty()) { val addresses = if (haAddressPool.isEmpty()) {
listOf(hostAndPort!!) listOf(hostAndPort!!)
} else { } else {
@ -549,23 +465,9 @@ class CordaRPCClient private constructor(
} }
return if (gracefulReconnect != null) { return if (gracefulReconnect != null) {
CordaRPCConnection.createWithGracefulReconnection( CordaRPCConnection.createWithGracefulReconnection(username, password, addresses, configuration, gracefulReconnect)
username,
password,
addresses,
configuration,
gracefulReconnect,
sslConfiguration
)
} else { } else {
CordaRPCConnection(getRpcClient().start( CordaRPCConnection(getRpcClient().start(InternalCordaRPCOps::class.java, username, password, externalTrace, impersonatedActor, targetLegalIdentity))
InternalCordaRPCOps::class.java,
username,
password,
externalTrace,
impersonatedActor,
targetLegalIdentity
))
} }
} }

View File

@ -32,24 +32,37 @@ import java.util.concurrent.TimeUnit
/** /**
* Wrapper over [CordaRPCOps] that handles exceptions when the node or the connection to the node fail. * Wrapper over [CordaRPCOps] that handles exceptions when the node or the connection to the node fail.
* *
* All operations are retried on failure, except flow start operations that die before receiving a valid [FlowHandle], in which case a * All operations are retried on failure, except flow start operations that die before receiving a valid [FlowHandle], in which case a [CouldNotStartFlowException] is thrown.
* [CouldNotStartFlowException] is thrown.
* *
* When calling methods that return a [DataFeed] like [CordaRPCOps.vaultTrackBy], the returned [DataFeed.updates] object will no longer * When calling methods that return a [DataFeed] like [CordaRPCOps.vaultTrackBy], the returned [DataFeed.updates] object will no longer
* be a usable [rx.Observable] but an instance of [ReconnectingObservable]. * be a usable [rx.Observable] but an instance of [ReconnectingObservable].
* The caller has to explicitly cast to [ReconnectingObservable] and call [ReconnectingObservable.subscribe]. If used as an [rx.Observable] * The caller has to explicitly cast to [ReconnectingObservable] and call [ReconnectingObservable.subscribe]. If used as an [rx.Observable] it will just fail.
* it will just fail.
* The returned [DataFeed.snapshot] is the snapshot as it was when the feed was first retrieved. * The returned [DataFeed.snapshot] is the snapshot as it was when the feed was first retrieved.
* *
* Note: There is no guarantee that observations will not be lost. * Note: There is no guarantee that observations will not be lost.
* *
* *This class is not a stable API. Any project that wants to use it, must copy and paste it.* * *This class is not a stable API. Any project that wants to use it, must copy and paste it.*
*/ */
// TODO The executor service is not needed. All we need is a single thread that deals with reconnecting and onto which // TODO The executor service is not needed. All we need is a single thread that deals with reconnecting and onto which ReconnectingObservables
// ReconnectingObservables and other things can attach themselves as listeners for reconnect events. // and other things can attach themselves as listeners for reconnect events.
class ReconnectingCordaRPCOps private constructor( class ReconnectingCordaRPCOps private constructor(
val reconnectingRPCConnection: ReconnectingRPCConnection val reconnectingRPCConnection: ReconnectingRPCConnection,
) : InternalCordaRPCOps by proxy(reconnectingRPCConnection) { private val observersPool: ExecutorService,
private val userPool: Boolean
) : AutoCloseable, InternalCordaRPCOps by proxy(reconnectingRPCConnection, observersPool) {
// Constructors that mirror CordaRPCClient.
constructor(
nodeHostAndPort: NetworkHostAndPort,
username: String,
password: String,
rpcConfiguration: CordaRPCClientConfiguration,
sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null,
observersPool: ExecutorService? = null
) : this(
ReconnectingRPCConnection(listOf(nodeHostAndPort), username, password, rpcConfiguration, sslConfiguration, classLoader),
observersPool ?: Executors.newCachedThreadPool(),
observersPool != null)
constructor( constructor(
nodeHostAndPorts: List<NetworkHostAndPort>, nodeHostAndPorts: List<NetworkHostAndPort>,
username: String, username: String,
@ -58,23 +71,18 @@ class ReconnectingCordaRPCOps private constructor(
gracefulReconnect: GracefulReconnect? = null, gracefulReconnect: GracefulReconnect? = null,
sslConfiguration: ClientRpcSslOptions? = null, sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null, classLoader: ClassLoader? = null,
observersPool: ExecutorService observersPool: ExecutorService? = null
) : this(ReconnectingRPCConnection( ) : this(
nodeHostAndPorts, ReconnectingRPCConnection(nodeHostAndPorts, username, password, rpcConfiguration, sslConfiguration, classLoader, gracefulReconnect),
username, observersPool ?: Executors.newCachedThreadPool(),
password, observersPool != null)
rpcConfiguration,
sslConfiguration,
classLoader,
gracefulReconnect,
observersPool))
private companion object { private companion object {
private val log = contextLogger() private val log = contextLogger()
private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection): InternalCordaRPCOps { private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection, observersPool: ExecutorService): InternalCordaRPCOps {
return Proxy.newProxyInstance( return Proxy.newProxyInstance(
this::class.java.classLoader, this::class.java.classLoader,
arrayOf(InternalCordaRPCOps::class.java), arrayOf(InternalCordaRPCOps::class.java),
ErrorInterceptingHandler(reconnectingRPCConnection)) as InternalCordaRPCOps ErrorInterceptingHandler(reconnectingRPCConnection, observersPool)) as InternalCordaRPCOps
} }
} }
private val retryFlowsPool = Executors.newScheduledThreadPool(1) private val retryFlowsPool = Executors.newScheduledThreadPool(1)
@ -117,8 +125,7 @@ class ReconnectingCordaRPCOps private constructor(
val rpcConfiguration: CordaRPCClientConfiguration, val rpcConfiguration: CordaRPCClientConfiguration,
val sslConfiguration: ClientRpcSslOptions? = null, val sslConfiguration: ClientRpcSslOptions? = null,
val classLoader: ClassLoader?, val classLoader: ClassLoader?,
val gracefulReconnect: GracefulReconnect? = null, val gracefulReconnect: GracefulReconnect? = null
val observersPool: ExecutorService
) : RPCConnection<CordaRPCOps> { ) : RPCConnection<CordaRPCOps> {
private var currentRPCConnection: CordaRPCConnection? = null private var currentRPCConnection: CordaRPCConnection? = null
enum class CurrentState { enum class CurrentState {
@ -171,18 +178,12 @@ class ReconnectingCordaRPCOps private constructor(
return currentRPCConnection!! return currentRPCConnection!!
} }
private tailrec fun establishConnectionWithRetry( private tailrec fun establishConnectionWithRetry(retryInterval: Duration = 1.seconds, roundRobinIndex: Int = 0): CordaRPCConnection {
retryInterval: Duration = 1.seconds,
roundRobinIndex: Int = 0
): CordaRPCConnection {
val attemptedAddress = nodeHostAndPorts[roundRobinIndex] val attemptedAddress = nodeHostAndPorts[roundRobinIndex]
log.info("Connecting to: $attemptedAddress") log.info("Connecting to: $attemptedAddress")
try { try {
return CordaRPCClient( return CordaRPCClient(
attemptedAddress, attemptedAddress, rpcConfiguration.copy(connectionMaxRetryInterval = retryInterval, maxReconnectAttempts = 1), sslConfiguration, classLoader
rpcConfiguration.copy(connectionMaxRetryInterval = retryInterval, maxReconnectAttempts = 1),
sslConfiguration,
classLoader
).start(username, password).also { ).start(username, password).also {
// Check connection is truly operational before returning it. // Check connection is truly operational before returning it.
require(it.proxy.nodeInfo().legalIdentitiesAndCerts.isNotEmpty()) { require(it.proxy.nodeInfo().legalIdentitiesAndCerts.isNotEmpty()) {
@ -239,7 +240,7 @@ class ReconnectingCordaRPCOps private constructor(
currentRPCConnection?.close() currentRPCConnection?.close()
} }
} }
private class ErrorInterceptingHandler(val reconnectingRPCConnection: ReconnectingRPCConnection) : InvocationHandler { private class ErrorInterceptingHandler(val reconnectingRPCConnection: ReconnectingRPCConnection, val observersPool: ExecutorService) : InvocationHandler {
private fun Method.isStartFlow() = name.startsWith("startFlow") || name.startsWith("startTrackedFlow") private fun Method.isStartFlow() = name.startsWith("startFlow") || name.startsWith("startTrackedFlow")
private fun checkIfIsStartFlow(method: Method, e: InvocationTargetException) { private fun checkIfIsStartFlow(method: Method, e: InvocationTargetException) {
@ -289,7 +290,7 @@ class ReconnectingCordaRPCOps private constructor(
DataFeed::class.java -> { DataFeed::class.java -> {
// Intercept the data feed methods and return a ReconnectingObservable instance // Intercept the data feed methods and return a ReconnectingObservable instance
val initialFeed: DataFeed<Any, Any?> = uncheckedCast(doInvoke(method, args)) val initialFeed: DataFeed<Any, Any?> = uncheckedCast(doInvoke(method, args))
val observable = ReconnectingObservable(reconnectingRPCConnection, initialFeed) { val observable = ReconnectingObservable(reconnectingRPCConnection, observersPool, initialFeed) {
// This handles reconnecting and creates new feeds. // This handles reconnecting and creates new feeds.
uncheckedCast(this.invoke(reconnectingRPCConnection.proxy, method, args)) uncheckedCast(this.invoke(reconnectingRPCConnection.proxy, method, args))
} }
@ -301,7 +302,8 @@ class ReconnectingCordaRPCOps private constructor(
} }
} }
fun close() { override fun close() {
if (!userPool) observersPool.shutdown()
retryFlowsPool.shutdown() retryFlowsPool.shutdown()
reconnectingRPCConnection.forceClose() reconnectingRPCConnection.forceClose()
} }

View File

@ -4,18 +4,21 @@ import net.corda.core.messaging.DataFeed
import rx.Observable import rx.Observable
import rx.Subscriber import rx.Subscriber
import rx.Subscription import rx.Subscription
import java.util.concurrent.ExecutorService
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
class ReconnectingObservable<T> private constructor(subscriber: ReconnectingSubscriber<T>) : Observable<T>(subscriber) { class ReconnectingObservable<T> private constructor(subscriber: ReconnectingSubscriber<T>) : Observable<T>(subscriber) {
constructor( constructor(
reconnectingRPCConnection: ReconnectingCordaRPCOps.ReconnectingRPCConnection, reconnectingRPCConnection: ReconnectingCordaRPCOps.ReconnectingRPCConnection,
executor: ExecutorService,
initialDataFeed: DataFeed<*, T>, initialDataFeed: DataFeed<*, T>,
createDataFeed: () -> DataFeed<*, T> createDataFeed: () -> DataFeed<*, T>
) : this(ReconnectingSubscriber(reconnectingRPCConnection, initialDataFeed, createDataFeed)) ) : this(ReconnectingSubscriber(reconnectingRPCConnection, executor, initialDataFeed, createDataFeed))
private class ReconnectingSubscriber<T>( private class ReconnectingSubscriber<T>(
private val reconnectingRPCConnection: ReconnectingCordaRPCOps.ReconnectingRPCConnection, private val reconnectingRPCConnection: ReconnectingCordaRPCOps.ReconnectingRPCConnection,
private val executor: ExecutorService,
private val initialDataFeed: DataFeed<*, T>, private val initialDataFeed: DataFeed<*, T>,
private val createDataFeed: () -> DataFeed<*, T> private val createDataFeed: () -> DataFeed<*, T>
) : OnSubscribe<T>, Subscription { ) : OnSubscribe<T>, Subscription {
@ -56,7 +59,7 @@ class ReconnectingObservable<T> private constructor(subscriber: ReconnectingSubs
private fun scheduleResubscribe(error: Throwable) { private fun scheduleResubscribe(error: Throwable) {
if (unsubscribed) return if (unsubscribed) return
reconnectingRPCConnection.observersPool.execute { executor.execute {
if (unsubscribed) return@execute if (unsubscribed) return@execute
reconnectingRPCConnection.reconnectOnError(error) reconnectingRPCConnection.reconnectOnError(error)
val newDataFeed = createDataFeed() val newDataFeed = createDataFeed()

View File

@ -366,14 +366,7 @@ class Verifier(val ltx: LedgerTransaction, private val transactionClassLoader: C
val contractInstances: List<Contract> = contractClasses.map { (contractClassName, contractClass) -> val contractInstances: List<Contract> = contractClasses.map { (contractClassName, contractClass) ->
try { try {
/** contractClass.getDeclaredConstructor().newInstance()
* This function must execute with the DJVM's sandbox, which does not
* permit user code to access [java.lang.reflect.Constructor] objects.
*
* [Class.newInstance] is deprecated as of Java 9.
*/
@Suppress("deprecation")
contractClass.newInstance()
} catch (e: Exception) { } catch (e: Exception) {
throw TransactionVerificationException.ContractCreationError(ltx.id, contractClassName, e) throw TransactionVerificationException.ContractCreationError(ltx.id, contractClassName, e)
} }

View File

@ -8,7 +8,6 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.schemas.StatePersistable import net.corda.core.schemas.StatePersistable
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -83,7 +82,6 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
open val participants: List<AbstractParty>? = null open val participants: List<AbstractParty>? = null
abstract val contractStateTypes: Set<Class<out ContractState>>? abstract val contractStateTypes: Set<Class<out ContractState>>?
open val externalIds: List<UUID> = emptyList() open val externalIds: List<UUID> = emptyList()
open val exactParticipants: List<AbstractParty>? = null
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> { override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this) return parser.parseCriteria(this)
} }
@ -103,23 +101,8 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
override val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(), override val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
override val constraints: Set<Vault.ConstraintInfo> = emptySet(), override val constraints: Set<Vault.ConstraintInfo> = emptySet(),
override val participants: List<AbstractParty>? = null, override val participants: List<AbstractParty>? = null,
override val externalIds: List<UUID> = emptyList(), override val externalIds: List<UUID> = emptyList()
override val exactParticipants: List<AbstractParty>? = null
) : CommonQueryCriteria() { ) : CommonQueryCriteria() {
@DeprecatedConstructorForDeserialization(version = 7)
constructor(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
stateRefs: List<StateRef>? = null,
notary: List<AbstractParty>? = null,
softLockingCondition: SoftLockingCondition? = null,
timeCondition: TimeCondition? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
constraints: Set<Vault.ConstraintInfo> = emptySet(),
participants: List<AbstractParty>? = null,
externalIds: List<UUID> = emptyList()
) : this(status, contractStateTypes, stateRefs, notary, softLockingCondition, timeCondition, relevancyStatus, constraintTypes, constraints, participants, externalIds, null)
// V4 constructors. // V4 constructors.
@DeprecatedConstructorForDeserialization(version = 7) @DeprecatedConstructorForDeserialization(version = 7)
constructor( constructor(
@ -179,36 +162,6 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withConstraints(constraints: Set<Vault.ConstraintInfo>): VaultQueryCriteria = copy(constraints = constraints) fun withConstraints(constraints: Set<Vault.ConstraintInfo>): VaultQueryCriteria = copy(constraints = constraints)
fun withParticipants(participants: List<AbstractParty>): VaultQueryCriteria = copy(participants = participants) fun withParticipants(participants: List<AbstractParty>): VaultQueryCriteria = copy(participants = participants)
fun withExternalIds(externalIds: List<UUID>): VaultQueryCriteria = copy(externalIds = externalIds) fun withExternalIds(externalIds: List<UUID>): VaultQueryCriteria = copy(externalIds = externalIds)
fun withExactParticipants(exactParticipants: List<AbstractParty>): VaultQueryCriteria = copy(exactParticipants = exactParticipants)
fun copy(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
stateRefs: List<StateRef>? = null,
notary: List<AbstractParty>? = null,
softLockingCondition: SoftLockingCondition? = null,
timeCondition: TimeCondition? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
constraints: Set<Vault.ConstraintInfo> = emptySet(),
participants: List<AbstractParty>? = null,
externalIds: List<UUID> = emptyList()
): VaultQueryCriteria {
return VaultQueryCriteria(
status,
contractStateTypes,
stateRefs,
notary,
softLockingCondition,
timeCondition,
relevancyStatus,
constraintTypes,
constraints,
participants,
externalIds,
exactParticipants
)
}
fun copy( fun copy(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
@ -265,24 +218,13 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
* LinearStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultLinearState] * LinearStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultLinearState]
*/ */
data class LinearStateQueryCriteria( data class LinearStateQueryCriteria(
override val participants: List<AbstractParty>? = null, override val participants: List<AbstractParty>?,
val uuid: List<UUID>? = null, val uuid: List<UUID>? = null,
val externalId: List<String>? = null, val externalId: List<String>? = null,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null, override val contractStateTypes: Set<Class<out ContractState>>? = null,
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
override val exactParticipants: List<AbstractParty>?
) : CommonQueryCriteria() { ) : CommonQueryCriteria() {
// V4 c'tor
@DeprecatedConstructorForDeserialization(version = 4)
constructor(
participants: List<AbstractParty>? = null,
uuid: List<UUID>? = null,
externalId: List<String>? = null,
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
) : this(participants, uuid, externalId, status, contractStateTypes, relevancyStatus, null)
// V3 c'tor // V3 c'tor
@JvmOverloads @JvmOverloads
@DeprecatedConstructorForDeserialization(version = 2) @DeprecatedConstructorForDeserialization(version = 2)
@ -323,8 +265,6 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withStatus(status: Vault.StateStatus): LinearStateQueryCriteria = copy(status = status) fun withStatus(status: Vault.StateStatus): LinearStateQueryCriteria = copy(status = status)
fun withContractStateTypes(contractStateTypes: Set<Class<out ContractState>>): LinearStateQueryCriteria = copy(contractStateTypes = contractStateTypes) fun withContractStateTypes(contractStateTypes: Set<Class<out ContractState>>): LinearStateQueryCriteria = copy(contractStateTypes = contractStateTypes)
fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): LinearStateQueryCriteria = copy(relevancyStatus = relevancyStatus) fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): LinearStateQueryCriteria = copy(relevancyStatus = relevancyStatus)
fun withExactParticipants(exactParticipants: List<AbstractParty>): LinearStateQueryCriteria =
copy(exactParticipants = exactParticipants)
fun copy( fun copy(
participants: List<AbstractParty>? = this.participants, participants: List<AbstractParty>? = this.participants,
@ -332,23 +272,6 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
externalId: List<String>? = this.externalId, externalId: List<String>? = this.externalId,
status: Vault.StateStatus = this.status, status: Vault.StateStatus = this.status,
contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes
): LinearStateQueryCriteria {
return LinearStateQueryCriteria(
participants,
uuid,
externalId,
status,
contractStateTypes
)
}
fun copy(
participants: List<AbstractParty>? = this.participants,
uuid: List<UUID>? = this.uuid,
externalId: List<String>? = this.externalId,
status: Vault.StateStatus = this.status,
contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes,
relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus
): LinearStateQueryCriteria { ): LinearStateQueryCriteria {
return LinearStateQueryCriteria( return LinearStateQueryCriteria(
participants, participants,
@ -394,21 +317,8 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
val issuerRef: List<OpaqueBytes>? = null, val issuerRef: List<OpaqueBytes>? = null,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null, override val contractStateTypes: Set<Class<out ContractState>>? = null,
override val relevancyStatus: Vault.RelevancyStatus, override val relevancyStatus: Vault.RelevancyStatus
override val exactParticipants: List<AbstractParty>? = null
) : CommonQueryCriteria() { ) : CommonQueryCriteria() {
// V4 c'tor
@DeprecatedConstructorForDeserialization(version = 1)
constructor(
participants: List<AbstractParty>? = null,
owner: List<AbstractParty>? = null,
quantity: ColumnPredicate<Long>? = null,
issuer: List<AbstractParty>? = null,
issuerRef: List<OpaqueBytes>? = null,
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
relevancyStatus: Vault.RelevancyStatus
) : this(participants, owner, quantity, issuer, issuerRef, status, contractStateTypes, relevancyStatus, null)
@JvmOverloads @JvmOverloads
@DeprecatedConstructorForDeserialization(version = 1) @DeprecatedConstructorForDeserialization(version = 1)
constructor( constructor(
@ -434,30 +344,6 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withStatus(status: Vault.StateStatus): FungibleAssetQueryCriteria = copy(status = status) fun withStatus(status: Vault.StateStatus): FungibleAssetQueryCriteria = copy(status = status)
fun withContractStateTypes(contractStateTypes: Set<Class<out ContractState>>): FungibleAssetQueryCriteria = copy(contractStateTypes = contractStateTypes) fun withContractStateTypes(contractStateTypes: Set<Class<out ContractState>>): FungibleAssetQueryCriteria = copy(contractStateTypes = contractStateTypes)
fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): FungibleAssetQueryCriteria = copy(relevancyStatus = relevancyStatus) fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): FungibleAssetQueryCriteria = copy(relevancyStatus = relevancyStatus)
fun withExactParticipants(exactParticipants: List<AbstractParty>): FungibleAssetQueryCriteria
= copy(exactParticipants = exactParticipants)
fun copy(
participants: List<AbstractParty>? = this.participants,
owner: List<AbstractParty>? = this.owner,
quantity: ColumnPredicate<Long>? = this.quantity,
issuer: List<AbstractParty>? = this.issuer,
issuerRef: List<OpaqueBytes>? = this.issuerRef,
status: Vault.StateStatus = this.status,
contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes,
relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus
): FungibleAssetQueryCriteria {
return FungibleAssetQueryCriteria(
participants,
owner,
quantity,
issuer,
issuerRef,
status,
contractStateTypes,
relevancyStatus
)
}
fun copy( fun copy(
participants: List<AbstractParty>? = this.participants, participants: List<AbstractParty>? = this.participants,

File diff suppressed because one or more lines are too long

View File

@ -86,8 +86,7 @@ There are four implementations of this interface which can be chained together t
1. ``VaultQueryCriteria`` provides filterable criteria on attributes within the Vault states table: status (UNCONSUMED, 1. ``VaultQueryCriteria`` provides filterable criteria on attributes within the Vault states table: status (UNCONSUMED,
CONSUMED), state reference(s), contract state type(s), notaries, soft locked states, timestamps (RECORDED, CONSUMED), CONSUMED), state reference(s), contract state type(s), notaries, soft locked states, timestamps (RECORDED, CONSUMED),
state constraints (see :ref:`Constraint Types <implicit_constraint_types>`), relevancy (ALL, RELEVANT, NON_RELEVANT), state constraints (see :ref:`Constraint Types <implicit_constraint_types>`), relevancy (ALL, RELEVANT, NON_RELEVANT).
participants (exact or any match).
.. note:: Sensible defaults are defined for frequently used attributes (status = UNCONSUMED, always include soft .. note:: Sensible defaults are defined for frequently used attributes (status = UNCONSUMED, always include soft
locked states). locked states).
@ -95,7 +94,7 @@ There are four implementations of this interface which can be chained together t
2. ``FungibleAssetQueryCriteria`` provides filterable criteria on attributes defined in the Corda Core 2. ``FungibleAssetQueryCriteria`` provides filterable criteria on attributes defined in the Corda Core
``FungibleAsset`` contract state interface, used to represent assets that are fungible, countable and issued by a ``FungibleAsset`` contract state interface, used to represent assets that are fungible, countable and issued by a
specific party (eg. ``Cash.State`` and ``CommodityContract.State`` in the Corda finance module). Filterable specific party (eg. ``Cash.State`` and ``CommodityContract.State`` in the Corda finance module). Filterable
attributes include: participants (exact or any match), owner(s), quantity, issuer party(s) and issuer reference(s). attributes include: participants(s), owner(s), quantity, issuer party(s) and issuer reference(s).
.. note:: All contract states that extend the ``FungibleAsset`` now automatically persist that interfaces common .. note:: All contract states that extend the ``FungibleAsset`` now automatically persist that interfaces common
state attributes to the **vault_fungible_states** table. state attributes to the **vault_fungible_states** table.
@ -103,7 +102,7 @@ There are four implementations of this interface which can be chained together t
3. ``LinearStateQueryCriteria`` provides filterable criteria on attributes defined in the Corda Core ``LinearState`` 3. ``LinearStateQueryCriteria`` provides filterable criteria on attributes defined in the Corda Core ``LinearState``
and ``DealState`` contract state interfaces, used to represent entities that continuously supersede themselves, all and ``DealState`` contract state interfaces, used to represent entities that continuously supersede themselves, all
of which share the same ``linearId`` (e.g. trade entity states such as the ``IRSState`` defined in the SIMM of which share the same ``linearId`` (e.g. trade entity states such as the ``IRSState`` defined in the SIMM
valuation demo). Filterable attributes include: participants (exact or any match), linearId(s), uuid(s), and externalId(s). valuation demo). Filterable attributes include: participant(s), linearId(s), uuid(s), and externalId(s).
.. note:: All contract states that extend ``LinearState`` or ``DealState`` now automatically persist those .. note:: All contract states that extend ``LinearState`` or ``DealState`` now automatically persist those
interfaces common state attributes to the **vault_linear_states** table. interfaces common state attributes to the **vault_linear_states** table.
@ -293,7 +292,7 @@ Query for unconsumed states for a given notary:
:end-before: DOCEND VaultQueryExample4 :end-before: DOCEND VaultQueryExample4
:dedent: 12 :dedent: 12
Query for unconsumed states for a given set of participants (matches any state that contains at least one of the specified participants): Query for unconsumed states for a given set of participants:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt .. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin :language: kotlin
@ -301,14 +300,6 @@ Query for unconsumed states for a given set of participants (matches any state t
:end-before: DOCEND VaultQueryExample5 :end-before: DOCEND VaultQueryExample5
:dedent: 12 :dedent: 12
Query for unconsumed states for a given set of participants (exactly matches only states that contain all specified participants):
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin
:start-after: DOCSTART VaultQueryExample51
:end-before: DOCEND VaultQueryExample51
:dedent: 12
Query for unconsumed states recorded between two time intervals: Query for unconsumed states recorded between two time intervals:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt .. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
@ -373,7 +364,7 @@ Query for unconsumed deal states with deals references:
:end-before: DOCEND VaultQueryExample10 :end-before: DOCEND VaultQueryExample10
:dedent: 12 :dedent: 12
Query for unconsumed deal states with deals parties (any match): Query for unconsumed deal states with deals parties:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt .. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin :language: kotlin
@ -381,14 +372,6 @@ Query for unconsumed deal states with deals parties (any match):
:end-before: DOCEND VaultQueryExample11 :end-before: DOCEND VaultQueryExample11
:dedent: 12 :dedent: 12
Query for unconsumed deal states with deals parties (exact match):
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin
:start-after: DOCSTART VaultQueryExample52
:end-before: DOCEND VaultQueryExample52
:dedent: 12
Query for only relevant linear states in the vault: Query for only relevant linear states in the vault:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt .. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

View File

@ -9,8 +9,6 @@ Unreleased
* Moved and renamed the testing web server to the ``testing`` subproject. Also renamed the published artifact to ``corda-testserver.jar``. * Moved and renamed the testing web server to the ``testing`` subproject. Also renamed the published artifact to ``corda-testserver.jar``.
* New Vault Query criteria to specify exact matches for specified participants.
* Support for Java 11 (compatibility mode). Please read https://github.com/corda/corda/pull/5356. * Support for Java 11 (compatibility mode). Please read https://github.com/corda/corda/pull/5356.
* Updating FinalityFlow with functionality to indicate the appropriate StatesToRecord. This allows the initiating party to record states * Updating FinalityFlow with functionality to indicate the appropriate StatesToRecord. This allows the initiating party to record states

View File

@ -5,9 +5,8 @@ This document explains the coding style used in the Corda repository. You will b
recommendations when submitting patches for review. Please take the time to read them and internalise them, to save recommendations when submitting patches for review. Please take the time to read them and internalise them, to save
time during code review. time during code review.
What follows are mostly *recommendations* and not *rules*. They are in places intentionally vague, so use your good judgement What follows are *recommendations* and not *rules*. They are in places intentionally vague, so use your good judgement
when interpreting them. The rules that are currently being enforced via the Detekt PR gateway can be found `here when interpreting them.
<https://github.com/corda/corda/blob/release/os/4.3/detekt-config.yml>`_.
1. General style 1. General style
################ ################
@ -36,9 +35,10 @@ that doesn't mean it's always better. In particular:
1.1 Line Length and Spacing 1.1 Line Length and Spacing
--------------------------- ---------------------------
We aim for line widths of no more than 140 characters. That is wide enough to avoid lots of pointless wrapping but We aim for line widths of no more than 120 characters. That is wide enough to avoid lots of pointless wrapping but
narrow enough that with a widescreen monitor and a 12 point fixed width font (like Menlo) you can fit two files narrow enough that with a widescreen monitor and a 12 point fixed width font (like Menlo) you can fit two files
next to each other. This is a rule that we enforce. next to each other. This is not a rigidly enforced rule and if wrapping a line would be excessively awkward, let it
overflow. Overflow of a few characters here and there isn't a big deal: the goal is general convenience.
Where the number of parameters in a function, class, etc. causes an overflow past the end of the first line, they should Where the number of parameters in a function, class, etc. causes an overflow past the end of the first line, they should
be structured one parameter per line. be structured one parameter per line.

View File

@ -133,8 +133,7 @@ class RpcReconnectTests {
} }
val reconnect = GracefulReconnect(onDisconnect = { numDisconnects++ }, onReconnect = onReconnect) val reconnect = GracefulReconnect(onDisconnect = { numDisconnects++ }, onReconnect = onReconnect)
val client = CordaRPCClient(addressesForRpc) val client = CordaRPCClient(addressesForRpc)
val bankAReconnectingRPCConnection = client.start(demoUser.username, demoUser.password, gracefulReconnect = reconnect) val bankAReconnectingRpc = client.start(demoUser.username, demoUser.password, gracefulReconnect = reconnect).proxy as ReconnectingCordaRPCOps
val bankAReconnectingRpc = bankAReconnectingRPCConnection.proxy as ReconnectingCordaRPCOps
// DOCEND rpcReconnectingRPC // DOCEND rpcReconnectingRPC
// Observe the vault and collect the observations. // Observe the vault and collect the observations.
@ -331,7 +330,7 @@ class RpcReconnectTests {
// Stop the observers. // Stop the observers.
vaultSubscription.unsubscribe() vaultSubscription.unsubscribe()
stateMachineSubscription.unsubscribe() stateMachineSubscription.unsubscribe()
bankAReconnectingRPCConnection.close() bankAReconnectingRpc.close()
} }
proxy.close() proxy.close()

View File

@ -471,12 +471,8 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
predicateSet.add(criteriaBuilder.and(vaultFungibleStatesRoot.get<ByteArray>("issuerRef").`in`(issuerRefs))) predicateSet.add(criteriaBuilder.and(vaultFungibleStatesRoot.get<ByteArray>("issuerRef").`in`(issuerRefs)))
} }
if (criteria.participants != null && criteria.exactParticipants != null)
throw VaultQueryException("Cannot specify both participants (${criteria.participants}) and exactParticipants " +
"(${criteria.exactParticipants}).")
// Participants. // Participants.
if (criteria.participants != null || criteria.exactParticipants != null) { criteria.participants?.let {
// Join VaultFungibleState and PersistentParty tables (participant values are added to the common query criteria predicate) // Join VaultFungibleState and PersistentParty tables (participant values are added to the common query criteria predicate)
val statePartyToFungibleStatesJoin = criteriaBuilder.and( val statePartyToFungibleStatesJoin = criteriaBuilder.and(
criteriaBuilder.equal(vaultFungibleStatesRoot.get<VaultSchemaV1.VaultFungibleStates>("stateRef"), criteriaBuilder.equal(vaultFungibleStatesRoot.get<VaultSchemaV1.VaultFungibleStates>("stateRef"),
@ -512,12 +508,8 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
predicateSet.add(criteriaBuilder.and(vaultLinearStatesRoot.get<String>("externalId").`in`(externalIds))) predicateSet.add(criteriaBuilder.and(vaultLinearStatesRoot.get<String>("externalId").`in`(externalIds)))
} }
if (criteria.participants != null && criteria.exactParticipants != null)
throw VaultQueryException("Cannot specify both participants (${criteria.participants}) " +
"and exactParticipants (${criteria.exactParticipants}).")
// Participants. // Participants.
if (criteria.participants != null || criteria.exactParticipants != null) { criteria.participants?.let {
// Join VaultLinearState and PersistentParty tables (participant values are added to the common query criteria predicate) // Join VaultLinearState and PersistentParty tables (participant values are added to the common query criteria predicate)
val statePartyToLinearStatesJoin = criteriaBuilder.and( val statePartyToLinearStatesJoin = criteriaBuilder.and(
criteriaBuilder.equal(vaultLinearStatesRoot.get<VaultSchemaV1.VaultLinearStates>("stateRef"), criteriaBuilder.equal(vaultLinearStatesRoot.get<VaultSchemaV1.VaultLinearStates>("stateRef"),
@ -710,38 +702,6 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
} }
} }
// Exact participants
// Requires a tricky SQL query to ensure *only* exact matches are selected (eg. a transaction cannot have more nor less than the
// exact participants specified in the query criteria).
criteria.exactParticipants?.let {
val exactParticipants = criteria.exactParticipants!!
// obtain all transactions where other participants are not present
val subQueryNotExists = criteriaQuery.subquery(Tuple::class.java)
val subRoot = subQueryNotExists.from(VaultSchemaV1.PersistentParty::class.java)
subQueryNotExists.select(subRoot.get("x500Name"))
subQueryNotExists.where(criteriaBuilder.and(
criteriaBuilder.equal(vaultStates.get<VaultSchemaV1.VaultStates>("stateRef"),
subRoot.get<VaultSchemaV1.PersistentParty>("compositeKey").get<PersistentStateRef>("stateRef"))),
criteriaBuilder.not(subRoot.get<VaultSchemaV1.PersistentParty>("x500Name").`in`(exactParticipants)))
val subQueryNotExistsPredicate = criteriaBuilder.and(criteriaBuilder.not(criteriaBuilder.exists(subQueryNotExists)))
constraintPredicates.add(subQueryNotExistsPredicate)
// join with transactions for each matching participant (only required where more than one)
if (exactParticipants.size > 1)
exactParticipants.forEach { participant ->
val subQueryExists = criteriaQuery.subquery(Tuple::class.java)
val subRootExists = subQueryExists.from(VaultSchemaV1.PersistentParty::class.java)
subQueryExists.select(subRootExists.get("x500Name"))
subQueryExists.where(criteriaBuilder.and(
criteriaBuilder.equal(vaultStates.get<VaultSchemaV1.VaultStates>("stateRef"),
subRootExists.get<VaultSchemaV1.PersistentParty>("compositeKey").get<PersistentStateRef>("stateRef"))),
criteriaBuilder.equal(subRootExists.get<VaultSchemaV1.PersistentParty>("x500Name"), participant))
val subQueryExistsPredicate = criteriaBuilder.and(criteriaBuilder.exists(subQueryExists))
constraintPredicates.add(subQueryExistsPredicate)
}
}
return emptySet() return emptySet()
} }

View File

@ -12,19 +12,23 @@
<include file="migration/node-core.changelog-v8.xml"/> <include file="migration/node-core.changelog-v8.xml"/>
<include file="migration/node-core.changelog-tx-mapping.xml"/> <include file="migration/node-core.changelog-tx-mapping.xml"/>
<include file="migration/node-core.changelog-v9.xml"/> <include file="migration/node-core.changelog-v9.xml"/>
<!-- This migration was originally written within the same script as node-core.changelog-v14-data. However, node-core.changelog-v10
runs a service with mapped schema which now has the new table so the table should be created before hand. The transfer of data
can then happen later in node-core.changelog-v14-data. -->
<include file="migration/node-core.changelog-v14-table.xml"/>
<include file="migration/node-core.changelog-v10.xml"/> <include file="migration/node-core.changelog-v10.xml"/>
<include file="migration/node-core.changelog-v11.xml"/> <include file="migration/node-core.changelog-v11.xml"/>
<!-- This migration was originally written within the same script as node-core.changelog-v14-data. However, node-core.changelog-v12
runs a service with mapped schema which now has the new table so the table should be created before hand. The transfer of data
can then happen later in node-core.changelog-v14-data. -->
<include file="migration/node-core.changelog-v14-table.xml"/>
<include file="migration/node-core.changelog-v12.xml"/> <include file="migration/node-core.changelog-v12.xml"/>
<!-- This migration (which creates extra columns in the transactions tables), must be run before the vault state migration (in <!-- This changeset (which creates extra columns in the transactions tables), must be run before the vault state migration (in
vault-schema.changelog-v9.xml), as that will use the current hibernate mappings, and those require all DB columns to be created. --> vault-schema.changelog-v9.xml), as that will use the current hibernate mappings, and those require all DB columns to be
created. -->
<include file="migration/node-core.changelog-v13.xml"/> <include file="migration/node-core.changelog-v13.xml"/>
<!-- This change should be done before the v14-data migration. --> <!-- This change should be done before the v14-data migration. -->
<include file="migration/node-core.changelog-v15-table.xml"/>
<include file="migration/node-core.changelog-v15.xml"/> <include file="migration/node-core.changelog-v15.xml"/>
<include file="migration/node-core.changelog-v16.xml"/>
<!-- This must run after node-core.changelog-init.xml, to prevent database columns being created twice. --> <!-- This must run after node-core.changelog-init.xml, to prevent database columns being created twice. -->
<include file="migration/vault-schema.changelog-v9.xml"/> <include file="migration/vault-schema.changelog-v9.xml"/>

View File

@ -20,18 +20,6 @@
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="modify identity_value column type" dbms="postgresql">
<addColumn tableName="node_identities">
<column name="temp_identity_value" type="varbinary(64000)">
<constraints nullable="true"/>
</column>
</addColumn>
<sql>UPDATE node_identities SET temp_identity_value = lo_get(identity_value)</sql>
<addNotNullConstraint tableName="node_identities" columnName="temp_identity_value"/>
<dropColumn tableName="node_identities" columnName="identity_value"/>
<renameColumn tableName="node_identities" oldColumnName="temp_identity_value" newColumnName="identity_value"/>
</changeSet>
<changeSet author="R3.Corda" id="migrate_identity_service_to_use_publicKey.toShortString()"> <changeSet author="R3.Corda" id="migrate_identity_service_to_use_publicKey.toShortString()">
<customChange class="net.corda.node.migration.PersistentIdentityMigration"> <customChange class="net.corda.node.migration.PersistentIdentityMigration">
</customChange> </customChange>

View File

@ -0,0 +1,19 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
logicalFilePath="migration/node-services.changelog-init.xml">
<changeSet author="R3.Corda" id="add-new-pk-hash-to-pk-table">
<createTable tableName="node_hash_to_key">
<column name="pk_hash" type="NVARCHAR(130)">
<constraints nullable="false"/>
</column>
<column name="public_key" type="blob">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="pk_hash" constraintName="node_hash_to_key_pk_hash" tableName="node_hash_to_key"/>
</changeSet>
</databaseChangeLog>

View File

@ -1,31 +1,25 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?> <?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
logicalFilePath="migration/node-services.changelog-init.xml">
<changeSet author="R3.Corda" id="add-new-pk-hash-to-pk-table" dbms="h2,mssql"> <changeSet author="R3.Corda" id="modify identity_value column type" dbms="postgresql">
<createTable tableName="node_hash_to_key">
<column name="pk_hash" type="NVARCHAR(130)">
<constraints nullable="false"/>
</column>
<column name="public_key" type="blob">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="pk_hash" constraintName="node_hash_to_key_pk_hash" tableName="node_hash_to_key"/>
</changeSet>
<changeSet author="R3.Corda" id="add-new-pk-hash-to-pk-table-postgresql" dbms="postgresql"> <preConditions onFail="MARK_RAN">
<createTable tableName="node_hash_to_key"> <not>
<column name="pk_hash" type="NVARCHAR(130)"> <sqlCheck expectedResult="bytea">
SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'node_identities' AND COLUMN_NAME = 'identity_value'
</sqlCheck>
</not>
</preConditions>
<dropColumn tableName="node_identities" columnName="identity_value"/>
<addColumn tableName="node_identities">
<column name="identity_value" type="varbinary(64000)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="public_key" type="varbinary(64000)"> </addColumn>
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="pk_hash" constraintName="node_hash_to_key_pk_hash" tableName="node_hash_to_key"/>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>

View File

@ -0,0 +1,21 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<changeSet author="R3.Corda" id="modify public_key column type" dbms="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="bytea">
SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'node_hash_to_key' AND COLUMN_NAME = 'public_key'
</sqlCheck>
</not>
</preConditions>
<dropColumn tableName="node_hash_to_key" columnName="public_key"/>
<addColumn tableName="node_hash_to_key">
<column name="public_key" type="varbinary(64000)">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@ -241,11 +241,6 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
val criteria = VaultQueryCriteria(participants = listOf(BIG_CORP)) val criteria = VaultQueryCriteria(participants = listOf(BIG_CORP))
val results = vaultService.queryBy<ContractState>(criteria) val results = vaultService.queryBy<ContractState>(criteria)
assertThat(results.states).hasSize(1) assertThat(results.states).hasSize(1)
// same query using strict participant matching
val strictCriteria = VaultQueryCriteria().withExactParticipants(listOf(BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(0) // all states include node identity (MEGA_CORP)
} }
} }
@ -259,11 +254,6 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
val criteria = VaultQueryCriteria(participants = listOf(MINI_CORP, BIG_CORP)) val criteria = VaultQueryCriteria(participants = listOf(MINI_CORP, BIG_CORP))
val results = vaultService.queryBy<ContractState>(criteria) val results = vaultService.queryBy<ContractState>(criteria)
assertThat(results.states).hasSize(2) assertThat(results.states).hasSize(2)
// same query using strict participant matching
val strictCriteria = VaultQueryCriteria().withExactParticipants(listOf(MEGA_CORP, BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
} }
} }
@ -805,48 +795,10 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY) identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY)
vaultFiller.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP)) vaultFiller.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP))
vaultFiller.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP)) vaultFiller.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP))
vaultFiller.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(MEGA_CORP)) vaultFiller.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP))
val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP))
val criteria = LinearStateQueryCriteria(participants = listOf(MEGA_CORP))
val results = vaultService.queryBy<ContractState>(criteria) val results = vaultService.queryBy<ContractState>(criteria)
assertThat(results.states).hasSize(5) assertThat(results.states).hasSize(3)
// same query using strict participant matching
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(listOf(MEGA_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(2)
}
}
@Test
fun `unconsumed dummy states for exact single participant`() {
database.transaction {
identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY)
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, MINI_CORP))
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, BIG_CORP))
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP)) // exact match
val strictCriteria = VaultQueryCriteria(exactParticipants = listOf(MEGA_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
}
}
@Test
fun `unconsumed dummy states for exact two participants`() {
database.transaction {
identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY)
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, MINI_CORP))
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, BIG_CORP)) // exact match
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP))
val strictCriteria = VaultQueryCriteria(exactParticipants = listOf(MEGA_CORP, BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
// same query using strict participant matching (unordered list of participants)
val strictCriteriaUnordered = VaultQueryCriteria(exactParticipants = listOf(BIG_CORP, MEGA_CORP))
val strictResultsUnordered = vaultService.queryBy<ContractState>(strictCriteriaUnordered)
assertThat(strictResultsUnordered.states).hasSize(1)
} }
} }
@ -861,19 +813,8 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP, MINI_CORP)) val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP, MINI_CORP))
val results = vaultService.queryBy<ContractState>(criteria) val results = vaultService.queryBy<ContractState>(criteria)
// DOCEND VaultQueryExample5 // DOCEND VaultQueryExample5
assertThat(results.states).hasSize(3) assertThat(results.states).hasSize(3)
// same query using strict participant matching
// DOCSTART VaultQueryExample51
val strictCriteria = LinearStateQueryCriteria(exactParticipants = listOf(MEGA_CORP, BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
// DOCEND VaultQueryExample51
assertThat(strictResults.states).hasSize(1)
// same query using strict participant matching (unordered list of participants)
val strictCriteriaUnordered = LinearStateQueryCriteria(exactParticipants = listOf(BIG_CORP, MEGA_CORP))
val strictResultsUnordered = vaultService.queryBy<ContractState>(strictCriteriaUnordered)
assertThat(strictResultsUnordered.states).hasSize(1)
} }
} }
@ -2018,13 +1959,6 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
// DOCEND VaultQueryExample11 // DOCEND VaultQueryExample11
assertThat(results.states).hasSize(1) assertThat(results.states).hasSize(1)
// same query using strict participant matching
// DOCSTART VaultQueryExample52
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(parties)
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
// DOCEND VaultQueryExample52
assertThat(strictResults.states).hasSize(0) // node identity included (MEGA_CORP)
} }
} }
@ -2418,11 +2352,6 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
assertThat(results.states).hasSize(1) assertThat(results.states).hasSize(1)
assertThat(results.states[0].state.data.linearId.externalId).isEqualTo("TEST1") assertThat(results.states[0].state.data.linearId.externalId).isEqualTo("TEST1")
// same query using strict participant matching
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(listOf(ALICE))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(0) // all states include node identity (MEGA_CORP)
} }
} }
@ -2440,11 +2369,6 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
assertThat(results.states).hasSize(1) assertThat(results.states).hasSize(1)
assertThat(results.states[0].state.data.linearId.externalId).isEqualTo("TEST1") assertThat(results.states[0].state.data.linearId.externalId).isEqualTo("TEST1")
// same query using strict participant matching
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(listOf(MEGA_CORP, ALICE, BOB, CHARLIE))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
} }
} }
@ -2477,7 +2401,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
} }
@Test @Test
fun `composite query for fungible, linear and dummy states for multiple participants`() { fun `composite query for fungible and linear states for multiple participants`() {
database.transaction { database.transaction {
identitySvc.verifyAndRegisterIdentity(ALICE_IDENTITY) identitySvc.verifyAndRegisterIdentity(ALICE_IDENTITY)
identitySvc.verifyAndRegisterIdentity(BOB_IDENTITY) identitySvc.verifyAndRegisterIdentity(BOB_IDENTITY)

View File

@ -1,8 +1,8 @@
package net.corda.bank.api package net.corda.bank.api
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.GracefulReconnect import net.corda.client.rpc.internal.ReconnectingCordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -35,9 +35,7 @@ object BankOfCordaClientApi {
* *
* @return a payment transaction (following successful issuance of cash to self). * @return a payment transaction (following successful issuance of cash to self).
*/ */
fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction { fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction = requestRPCIssueHA(listOf(rpcAddress), params)
return requestRPCIssueHA(listOf(rpcAddress), params)
}
/** /**
* RPC API * RPC API
@ -46,28 +44,20 @@ object BankOfCordaClientApi {
*/ */
fun requestRPCIssueHA(availableRpcServers: List<NetworkHostAndPort>, params: IssueRequestParams): SignedTransaction { fun requestRPCIssueHA(availableRpcServers: List<NetworkHostAndPort>, params: IssueRequestParams): SignedTransaction {
// TODO: privileged security controls required // TODO: privileged security controls required
CordaRPCClient(availableRpcServers) ReconnectingCordaRPCOps(availableRpcServers, BOC_RPC_USER, BOC_RPC_PWD, CordaRPCClientConfiguration.DEFAULT).use { rpc->
.start(BOC_RPC_USER, BOC_RPC_PWD, gracefulReconnect = GracefulReconnect()).use { rpc-> rpc.waitUntilNetworkReady().getOrThrow()
rpc.proxy.waitUntilNetworkReady().getOrThrow()
// Resolve parties via RPC // Resolve parties via RPC
val issueToParty = rpc.proxy.wellKnownPartyFromX500Name(params.issueToPartyName) val issueToParty = rpc.wellKnownPartyFromX500Name(params.issueToPartyName)
?: throw IllegalStateException("Unable to locate ${params.issueToPartyName} in Network Map Service") ?: throw IllegalStateException("Unable to locate ${params.issueToPartyName} in Network Map Service")
val notaryLegalIdentity = rpc.proxy.notaryIdentities().firstOrNull { it.name == params.notaryName } val notaryLegalIdentity = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName }
?: throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache") ?: throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache")
val anonymous = true val anonymous = true
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())
logger.info("${rpc.proxy.nodeInfo()} issuing ${params.amount} to transfer to $issueToParty ...") logger.info("${rpc.nodeInfo()} issuing ${params.amount} to transfer to $issueToParty ...")
return rpc.proxy.startFlow( return rpc.startFlow(::CashIssueAndPaymentFlow, params.amount, issuerBankPartyRef, issueToParty, anonymous, notaryLegalIdentity)
::CashIssueAndPaymentFlow,
params.amount,
issuerBankPartyRef,
issueToParty,
anonymous,
notaryLegalIdentity
)
.returnValue.getOrThrow().stx .returnValue.getOrThrow().stx
} }
} }

View File

@ -3,9 +3,8 @@ package net.corda.webserver.internal
import com.google.common.html.HtmlEscapers.htmlEscaper import com.google.common.html.HtmlEscapers.htmlEscaper
import io.netty.channel.unix.Errors import io.netty.channel.unix.Errors
import net.corda.client.jackson.JacksonSupport import net.corda.client.jackson.JacksonSupport
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.CordaRPCConnection import net.corda.client.rpc.internal.ReconnectingCordaRPCOps
import net.corda.client.rpc.GracefulReconnect
import net.corda.core.internal.errors.AddressBindingException import net.corda.core.internal.errors.AddressBindingException
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
@ -47,12 +46,8 @@ class NodeWebServer(val config: WebServerConfig) {
} }
fun run() { fun run() {
try { while (server.isRunning) {
while (server.isRunning) { Thread.sleep(100) // TODO: Redesign
Thread.sleep(100) // TODO: Redesign
}
} finally {
rpc.close()
} }
} }
@ -80,11 +75,7 @@ class NodeWebServer(val config: WebServerConfig) {
sslContextFactory.setIncludeProtocols("TLSv1.2") sslContextFactory.setIncludeProtocols("TLSv1.2")
sslContextFactory.setExcludeCipherSuites(".*NULL.*", ".*RC4.*", ".*MD5.*", ".*DES.*", ".*DSS.*") sslContextFactory.setExcludeCipherSuites(".*NULL.*", ".*RC4.*", ".*MD5.*", ".*DES.*", ".*DSS.*")
sslContextFactory.setIncludeCipherSuites(".*AES.*GCM.*") sslContextFactory.setIncludeCipherSuites(".*AES.*GCM.*")
val sslConnector = ServerConnector( val sslConnector = ServerConnector(server, SslConnectionFactory(sslContextFactory, "http/1.1"), HttpConnectionFactory(httpsConfiguration))
server,
SslConnectionFactory(sslContextFactory, "http/1.1"),
HttpConnectionFactory(httpsConfiguration)
)
sslConnector.port = address.port sslConnector.port = address.port
sslConnector sslConnector
} else { } else {
@ -184,21 +175,9 @@ class NodeWebServer(val config: WebServerConfig) {
} }
} }
private lateinit var rpc: CordaRPCConnection private fun reconnectingCordaRPCOps() = ReconnectingCordaRPCOps(config.rpcAddress, config.runAs.username , config.runAs.password, CordaRPCClientConfiguration.DEFAULT, null, javaClass.classLoader)
private fun reconnectingCordaRPCOps(): CordaRPCOps {
rpc = CordaRPCClient(config.rpcAddress, null, javaClass.classLoader)
.start(
config.runAs.username,
config.runAs.password,
GracefulReconnect()
)
return rpc.proxy
}
/** /** Fetch WebServerPluginRegistry classes registered in META-INF/services/net.corda.webserver.services.WebServerPluginRegistry files that exist in the classpath */
* Fetch WebServerPluginRegistry classes registered in META-INF/services/net.corda.webserver.services.WebServerPluginRegistry
* files that exist in the classpath
*/
val pluginRegistries: List<WebServerPluginRegistry> by lazy { val pluginRegistries: List<WebServerPluginRegistry> by lazy {
ServiceLoader.load(WebServerPluginRegistry::class.java).toList() ServiceLoader.load(WebServerPluginRegistry::class.java).toList()
} }

View File

@ -20,6 +20,7 @@ import net.corda.core.contracts.withoutIssuer
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -93,14 +94,15 @@ class NewTransaction : Fragment() {
initOwner(window) initOwner(window)
show() show()
} }
val handle: FlowHandle<AbstractCashFlow.Result> = when (request) {
is IssueAndPaymentRequest -> rpcProxy.value!!.startFlow(::CashIssueAndPaymentFlow, request)
is PaymentRequest -> rpcProxy.value!!.startFlow(::CashPaymentFlow, request)
is ExitRequest -> rpcProxy.value!!.startFlow(::CashExitFlow, request)
else -> throw IllegalArgumentException("Unexpected request type: $request")
}
runAsync { runAsync {
try { try {
when (request) { handle.returnValue.getOrThrow()
is IssueAndPaymentRequest -> rpcProxy.value!!.startFlow(::CashIssueAndPaymentFlow, request)
is PaymentRequest -> rpcProxy.value!!.startFlow(::CashPaymentFlow, request)
is ExitRequest -> rpcProxy.value!!.startFlow(::CashExitFlow, request)
else -> throw IllegalArgumentException("Unexpected request type: $request")
}.returnValue.getOrThrow()
} finally { } finally {
dialog.dialogPane.isDisable = false dialog.dialogPane.isDisable = false
} }

View File

@ -11,8 +11,8 @@ import net.corda.client.jackson.JacksonSupport
import net.corda.client.jackson.StringToMethodCallParser import net.corda.client.jackson.StringToMethodCallParser
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.GracefulReconnect
import net.corda.client.rpc.PermissionException import net.corda.client.rpc.PermissionException
import net.corda.client.rpc.internal.ReconnectingCordaRPCOps
import net.corda.core.CordaException import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
@ -22,6 +22,7 @@ import net.corda.core.internal.*
import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.messaging.InternalCordaRPCOps import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.packageName_
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.ANSIProgressRenderer
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
@ -90,24 +91,21 @@ object InteractiveShell {
fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null, standalone: Boolean = false) { fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null, standalone: Boolean = false) {
rpcOps = { username: String, password: String -> rpcOps = { username: String, password: String ->
val connection = if (standalone) { if (standalone) {
CordaRPCClient( ReconnectingCordaRPCOps(configuration.hostAndPort, username, password, CordaRPCClientConfiguration.DEFAULT, configuration.ssl, classLoader).also {
configuration.hostAndPort, rpcConn = it
configuration.ssl, }
classLoader
).start(username, password, gracefulReconnect = GracefulReconnect())
} else { } else {
CordaRPCClient( val client = CordaRPCClient(hostAndPort = configuration.hostAndPort,
hostAndPort = configuration.hostAndPort,
configuration = CordaRPCClientConfiguration.DEFAULT.copy( configuration = CordaRPCClientConfiguration.DEFAULT.copy(
maxReconnectAttempts = 1 maxReconnectAttempts = 1
), ),
sslConfiguration = configuration.ssl, sslConfiguration = configuration.ssl,
classLoader = classLoader classLoader = classLoader)
).start(username, password) val connection = client.start(username, password)
rpcConn = connection
connection.proxy as InternalCordaRPCOps
} }
rpcConn = connection
connection.proxy as InternalCordaRPCOps
} }
_startShell(configuration, classLoader) _startShell(configuration, classLoader)
} }