mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Refactored ErrorOr into Try, with Success and Failure data sub-classes, and moved it into core.utilities
This commit is contained in:
@ -1,11 +1,9 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionType
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.div
|
||||
@ -13,6 +11,7 @@ import net.corda.core.getOrThrow
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.Try
|
||||
import net.corda.flows.NotaryError
|
||||
import net.corda.flows.NotaryException
|
||||
import net.corda.flows.NotaryFlow
|
||||
@ -23,10 +22,10 @@ import net.corda.node.services.transactions.minClusterSize
|
||||
import net.corda.node.services.transactions.minCorrectReplicas
|
||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||
import net.corda.node.utilities.transaction
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.junit.After
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.nio.file.Files
|
||||
import kotlin.test.assertEquals
|
||||
@ -95,10 +94,10 @@ class BFTNotaryServiceTests {
|
||||
val flows = spendTxs.map { NotaryFlow.Client(it) }
|
||||
val stateMachines = flows.map { services.startFlow(it) }
|
||||
mockNet.runNetwork()
|
||||
val results = stateMachines.map { ErrorOr.catch { it.resultFuture.getOrThrow() } }
|
||||
val results = stateMachines.map { Try.on { it.resultFuture.getOrThrow() } }
|
||||
val successfulIndex = results.mapIndexedNotNull { index, result ->
|
||||
if (result.error == null) {
|
||||
val signers = result.getOrThrow().map { it.by }
|
||||
if (result is Try.Success) {
|
||||
val signers = result.value.map { it.by }
|
||||
assertEquals(minCorrectReplicas(clusterSize), signers.size)
|
||||
signers.forEach {
|
||||
assertTrue(it in (notary.owningKey as CompositeKey).leafKeys)
|
||||
@ -109,8 +108,8 @@ class BFTNotaryServiceTests {
|
||||
}
|
||||
}.single()
|
||||
spendTxs.zip(results).forEach { (tx, result) ->
|
||||
if (result.error != null) {
|
||||
val error = (result.error as NotaryException).error as NotaryError.Conflict
|
||||
if (result is Try.Failure) {
|
||||
val error = (result.exception as NotaryException).error as NotaryError.Conflict
|
||||
assertEquals(tx.id, error.txId)
|
||||
val (stateRef, consumingTx) = error.conflict.verified().stateHistory.entries.single()
|
||||
assertEquals(StateRef(issueTx.id, 0), stateRef)
|
||||
|
@ -12,15 +12,11 @@ import com.google.common.collect.HashMultimap
|
||||
import com.google.common.collect.Multimaps
|
||||
import com.google.common.collect.SetMultimap
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.seconds
|
||||
import net.corda.core.serialization.KryoPoolWithContext
|
||||
import net.corda.core.utilities.LazyStickyPool
|
||||
import net.corda.core.utilities.LifeCycle
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.services.RPCUserService
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
@ -43,7 +39,6 @@ import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Method
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
data class RPCServerConfiguration(
|
||||
/** The number of threads to use for handling RPC requests */
|
||||
@ -270,14 +265,7 @@ class RPCServer(
|
||||
)
|
||||
rpcExecutor!!.submit {
|
||||
val result = invokeRpc(rpcContext, clientToServer.methodName, clientToServer.arguments)
|
||||
val resultWithExceptionUnwrapped = result.mapError {
|
||||
if (it is InvocationTargetException) {
|
||||
it.cause ?: RPCException("Caught InvocationTargetException without cause")
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
sendReply(clientToServer.id, clientToServer.clientAddress, resultWithExceptionUnwrapped)
|
||||
sendReply(clientToServer.id, clientToServer.clientAddress, result)
|
||||
}
|
||||
}
|
||||
is RPCApi.ClientToServer.ObservablesClosed -> {
|
||||
@ -287,25 +275,24 @@ class RPCServer(
|
||||
artemisMessage.acknowledge()
|
||||
}
|
||||
|
||||
private fun invokeRpc(rpcContext: RpcContext, methodName: String, arguments: List<Any?>): ErrorOr<Any> {
|
||||
return ErrorOr.catch {
|
||||
private fun invokeRpc(rpcContext: RpcContext, methodName: String, arguments: List<Any?>): Try<Any> {
|
||||
return Try.on {
|
||||
try {
|
||||
CURRENT_RPC_CONTEXT.set(rpcContext)
|
||||
log.debug { "Calling $methodName" }
|
||||
val method = methodTable[methodName] ?:
|
||||
throw RPCException("Received RPC for unknown method $methodName - possible client/server version skew?")
|
||||
method.invoke(ops, *arguments.toTypedArray())
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.cause ?: RPCException("Caught InvocationTargetException without cause")
|
||||
} finally {
|
||||
CURRENT_RPC_CONTEXT.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendReply(requestId: RPCApi.RpcRequestId, clientAddress: SimpleString, resultWithExceptionUnwrapped: ErrorOr<Any>) {
|
||||
val reply = RPCApi.ServerToClient.RpcReply(
|
||||
id = requestId,
|
||||
result = resultWithExceptionUnwrapped
|
||||
)
|
||||
private fun sendReply(requestId: RPCApi.RpcRequestId, clientAddress: SimpleString, result: Try<Any>) {
|
||||
val reply = RPCApi.ServerToClient.RpcReply(requestId, result)
|
||||
val observableContext = ObservableContext(
|
||||
requestId,
|
||||
observableMap,
|
||||
|
@ -7,18 +7,14 @@ import co.paralleluniverse.strands.Strand
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import net.corda.core.DeclaredField.Companion.declaredField
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.abbreviate
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.services.api.FlowAppAuditEvent
|
||||
import net.corda.node.services.api.FlowPermissionAuditEvent
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
@ -71,7 +67,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
@Transient override lateinit var serviceHub: ServiceHubInternal
|
||||
@Transient internal lateinit var database: Database
|
||||
@Transient internal lateinit var actionOnSuspend: (FlowIORequest) -> Unit
|
||||
@Transient internal lateinit var actionOnEnd: (ErrorOr<R>, Boolean) -> Unit
|
||||
@Transient internal lateinit var actionOnEnd: (Try<R>, Boolean) -> Unit
|
||||
@Transient internal var fromCheckpoint: Boolean = false
|
||||
@Transient private var txTrampoline: Transaction? = null
|
||||
|
||||
@ -125,7 +121,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
.filter { it.state is FlowSessionState.Initiating }
|
||||
.forEach { it.waitForConfirmation() }
|
||||
// This is to prevent actionOnEnd being called twice if it throws an exception
|
||||
actionOnEnd(ErrorOr(result), false)
|
||||
actionOnEnd(Try.Success(result), false)
|
||||
_resultFuture?.set(result)
|
||||
logic.progressTracker?.currentStep = ProgressTracker.DONE
|
||||
logger.debug { "Flow finished with result $result" }
|
||||
@ -138,7 +134,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
}
|
||||
|
||||
private fun processException(exception: Throwable, propagated: Boolean) {
|
||||
actionOnEnd(ErrorOr.of(exception), propagated)
|
||||
actionOnEnd(Try.Failure(exception), propagated)
|
||||
_resultFuture?.setException(exception)
|
||||
logic.progressTracker?.endWithError(exception)
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ import com.google.common.collect.HashMultimap
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import io.requery.util.CloseableIterator
|
||||
import net.corda.core.*
|
||||
import net.corda.core.ThreadBox
|
||||
import net.corda.core.bufferUntilSubscribed
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.flows.FlowException
|
||||
@ -25,6 +26,8 @@ import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.then
|
||||
import net.corda.core.utilities.Try
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.trace
|
||||
@ -122,7 +125,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
abstract val logic: FlowLogic<*>
|
||||
|
||||
data class Add(override val logic: FlowLogic<*>) : Change()
|
||||
data class Removed(override val logic: FlowLogic<*>, val result: ErrorOr<*>) : Change()
|
||||
data class Removed(override val logic: FlowLogic<*>, val result: Try<*>) : Change()
|
||||
}
|
||||
|
||||
// A list of all the state machines being managed by this class. We expose snapshots of it via the stateMachines
|
||||
@ -442,13 +445,13 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
processIORequest(ioRequest)
|
||||
decrementLiveFibers()
|
||||
}
|
||||
fiber.actionOnEnd = { resultOrError, propagated ->
|
||||
fiber.actionOnEnd = { result, propagated ->
|
||||
try {
|
||||
mutex.locked {
|
||||
stateMachines.remove(fiber)?.let { checkpointStorage.removeCheckpoint(it) }
|
||||
notifyChangeObservers(Change.Removed(fiber.logic, resultOrError))
|
||||
notifyChangeObservers(Change.Removed(fiber.logic, result))
|
||||
}
|
||||
endAllFiberSessions(fiber, resultOrError.error, propagated)
|
||||
endAllFiberSessions(fiber, result, propagated)
|
||||
} finally {
|
||||
fiber.commitTransaction()
|
||||
decrementLiveFibers()
|
||||
@ -463,10 +466,10 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
}
|
||||
}
|
||||
|
||||
private fun endAllFiberSessions(fiber: FlowStateMachineImpl<*>, exception: Throwable?, propagated: Boolean) {
|
||||
private fun endAllFiberSessions(fiber: FlowStateMachineImpl<*>, result: Try<*>, propagated: Boolean) {
|
||||
openSessions.values.removeIf { session ->
|
||||
if (session.fiber == fiber) {
|
||||
session.endSession(exception, propagated)
|
||||
session.endSession((result as? Try.Failure)?.exception, propagated)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -1,20 +1,22 @@
|
||||
package net.corda.node.shell
|
||||
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.crypto.commonName
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.messaging.StateMachineUpdate.Added
|
||||
import net.corda.core.messaging.StateMachineUpdate.Removed
|
||||
import net.corda.core.then
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.Try
|
||||
import org.crsh.text.Color
|
||||
import org.crsh.text.Decoration
|
||||
import org.crsh.text.RenderPrintWriter
|
||||
import org.crsh.text.ui.LabelElement
|
||||
import org.crsh.text.ui.TableElement
|
||||
import org.crsh.text.ui.Overflow
|
||||
import org.crsh.text.ui.RowElement
|
||||
import org.crsh.text.ui.TableElement
|
||||
import rx.Subscriber
|
||||
|
||||
class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Subscriber<Any>() {
|
||||
@ -51,10 +53,10 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
future.setException(e)
|
||||
}
|
||||
|
||||
private fun stateColor(smmUpdate: StateMachineUpdate): Color {
|
||||
return when(smmUpdate){
|
||||
is StateMachineUpdate.Added -> Color.blue
|
||||
is StateMachineUpdate.Removed -> smmUpdate.result.match({ Color.green } , { Color.red })
|
||||
private fun stateColor(update: StateMachineUpdate): Color {
|
||||
return when (update) {
|
||||
is Added -> Color.blue
|
||||
is Removed -> if (update.result.isSuccess) Color.green else Color.red
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +70,7 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
// TODO Add progress tracker?
|
||||
private fun createStateMachinesRow(smmUpdate: StateMachineUpdate) {
|
||||
when (smmUpdate) {
|
||||
is StateMachineUpdate.Added -> {
|
||||
is Added -> {
|
||||
table.add(RowElement().add(
|
||||
LabelElement(formatFlowId(smmUpdate.id)),
|
||||
LabelElement(formatFlowName(smmUpdate.stateMachineInfo.flowLogicClassName)),
|
||||
@ -77,7 +79,7 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
).style(stateColor(smmUpdate).fg()))
|
||||
indexMap[smmUpdate.id] = table.rows.size - 1
|
||||
}
|
||||
is StateMachineUpdate.Removed -> {
|
||||
is Removed -> {
|
||||
val idx = indexMap[smmUpdate.id]
|
||||
if (idx != null) {
|
||||
val oldRow = table.rows[idx]
|
||||
@ -114,7 +116,7 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatFlowResult(flowResult: ErrorOr<*>): String {
|
||||
private fun formatFlowResult(flowResult: Try<*>): String {
|
||||
fun successFormat(value: Any?): String {
|
||||
return when(value) {
|
||||
is SignedTransaction -> "Tx ID: " + value.id.toString()
|
||||
@ -123,6 +125,9 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
else -> value.toString()
|
||||
}
|
||||
}
|
||||
return flowResult.match({ successFormat(it) }, { it.message ?: it.toString() })
|
||||
return when (flowResult) {
|
||||
is Try.Success -> successFormat(flowResult.value)
|
||||
is Try.Failure -> flowResult.exception.message ?: flowResult.exception.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user