From 71d8586e614fcb8f44f11b70f968273c2c717477 Mon Sep 17 00:00:00 2001 From: bpaunescu Date: Mon, 4 Jun 2018 10:14:53 +0100 Subject: [PATCH] =?UTF-8?q?ENT-1962:=20throw=20a=20clearer=20exception=20w?= =?UTF-8?q?hen=20starting=20RPC=20flow=20while=20SMM=20is=20stopped=20?= =?UTF-8?q?=E2=80=A6=20(#912)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ENT-1962: throw a clearer exception when starting RPC flow while SMM is stopped. * ENT-1962: update exception constructor --- .../main/kotlin/net/corda/core/internal/LifeCycle.kt | 11 +++++++++++ .../kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt | 7 ++++++- .../exceptions/StateMachineStoppedException.kt | 7 +++++++ .../statemachine/MultiThreadedStateMachineManager.kt | 3 ++- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 node/src/main/kotlin/net/corda/node/internal/exceptions/StateMachineStoppedException.kt diff --git a/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt b/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt index 4e71271875..e7e08908f7 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LifeCycle.kt @@ -39,6 +39,17 @@ class LifeCycle>(initial: S) { } fun requireState(requiredState: S) = requireState(requiredState) {} + fun requireState( + requiredState: S, + throwable: Throwable, + block: () -> A + ): A { + return lock.readLock().withLock { + if (requiredState != state) { throw throwable } + block() + } + } + /** Assert something about the current state atomically. */ fun requireState( errorMessage: (S) -> String, diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index b686c2fcaa..f883606ee8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -50,6 +50,7 @@ import net.corda.core.node.services.vault.Sort import net.corda.core.serialization.serialize import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow +import net.corda.node.internal.exceptions.StateMachineStoppedException import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.messaging.context @@ -185,7 +186,11 @@ internal class CordaRPCOpsImpl( if (isFlowsDrainingModeEnabled()) { throw RejectedCommandException("Node is draining before shutdown. Cannot start new flows through RPC.") } - return flowStarter.invokeFlowAsync(logicType, context(), *args).getOrThrow() + try { + return flowStarter.invokeFlowAsync(logicType, context(), *args).getOrThrow() + } catch (e: StateMachineStoppedException) { + throw RejectedCommandException("Node is shutting down. Cannot start new flows through RPC.") + } } override fun attachmentExists(id: SecureHash): Boolean { diff --git a/node/src/main/kotlin/net/corda/node/internal/exceptions/StateMachineStoppedException.kt b/node/src/main/kotlin/net/corda/node/internal/exceptions/StateMachineStoppedException.kt new file mode 100644 index 0000000000..88a5bcb31a --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/exceptions/StateMachineStoppedException.kt @@ -0,0 +1,7 @@ +package net.corda.node.internal.exceptions + +import net.corda.core.CordaRuntimeException + +class StateMachineStoppedException(message: String, cause: Throwable?) : CordaRuntimeException(message, cause) { + constructor(msg: String) : this(msg, null) +} diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/MultiThreadedStateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/MultiThreadedStateMachineManager.kt index eaeee67788..8aa56cf475 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/MultiThreadedStateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/MultiThreadedStateMachineManager.kt @@ -34,6 +34,7 @@ import net.corda.core.utilities.Try import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.node.internal.InitiatedFlowFactory +import net.corda.node.internal.exceptions.StateMachineStoppedException import net.corda.node.services.api.CheckpointStorage import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.shouldCheckCheckpoints @@ -196,7 +197,7 @@ class MultiThreadedStateMachineManager( ourIdentity: Party?, deduplicationHandler: DeduplicationHandler? ): CordaFuture> { - return lifeCycle.requireState(State.STARTED) { + return lifeCycle.requireState(State.STARTED, StateMachineStoppedException("Flow cannot be started. State machine is stopped.")) { startFlowInternal( invocationContext = context, flowLogic = flowLogic,