mirror of
https://github.com/corda/corda.git
synced 2025-02-21 01:42:24 +00:00
Throw a diagnostic exception if your FlowLogic.call method is not marked as @Suspendable.
This catches a bunch of unit tests where it's missing and also resolves an issue I saw Roger hit the other day.
This commit is contained in:
parent
b63df0ea39
commit
1139c1abf5
@ -235,14 +235,20 @@ class NodeSchedulerService(private val services: ServiceHubInternal,
|
||||
|
||||
private fun onTimeReached(scheduledState: ScheduledStateRef) {
|
||||
serverThread.execute {
|
||||
services.database.transaction {
|
||||
val scheduledFlow = getScheduledFlow(scheduledState)
|
||||
if (scheduledFlow != null) {
|
||||
val future = services.startFlow(scheduledFlow, FlowInitiator.Scheduled(scheduledState)).resultFuture
|
||||
future.then {
|
||||
unfinishedSchedules.countDown()
|
||||
var flowName: String? = "(unknown)"
|
||||
try {
|
||||
services.database.transaction {
|
||||
val scheduledFlow = getScheduledFlow(scheduledState)
|
||||
if (scheduledFlow != null) {
|
||||
flowName = scheduledFlow.javaClass.name
|
||||
val future = services.startFlow(scheduledFlow, FlowInitiator.Scheduled(scheduledState)).resultFuture
|
||||
future.then {
|
||||
unfinishedSchedules.countDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.error("Failed to start scheduled flow $flowName for $scheduledState due to an internal error", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node.services.statemachine
|
||||
|
||||
import co.paralleluniverse.fibers.Fiber
|
||||
import co.paralleluniverse.fibers.FiberExecutorScheduler
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import co.paralleluniverse.fibers.instrument.SuspendableHelper
|
||||
import co.paralleluniverse.strands.Strand
|
||||
import com.codahale.metrics.Gauge
|
||||
@ -427,6 +428,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
}
|
||||
|
||||
private fun initFiber(fiber: FlowStateMachineImpl<*>) {
|
||||
verifyFlowLogicIsSuspendable(fiber.logic)
|
||||
fiber.database = database
|
||||
fiber.serviceHub = serviceHub
|
||||
fiber.actionOnSuspend = { ioRequest ->
|
||||
@ -458,6 +460,19 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyFlowLogicIsSuspendable(logic: FlowLogic<Any?>) {
|
||||
// Quasar requires (in Java 8) that at least the call method be annotated suspendable. Unfortunately, it's
|
||||
// easy to forget to add this when creating a new flow, so we check here to give the user a better error.
|
||||
//
|
||||
// The Kotlin compiler can sometimes generate a synthetic bridge method from a single call declaration, which
|
||||
// forwards to the void method and then returns Unit. However annotations do not get copied across to this
|
||||
// bridge, so we have to do a more complex scan here.
|
||||
val call = logic.javaClass.methods.first { !it.isSynthetic && it.name == "call" && it.parameterCount == 0 }
|
||||
if (call.getAnnotation(Suspendable::class.java) == null) {
|
||||
throw FlowException("${logic.javaClass.name}.call() is not annotated as @Suspendable. Please fix this.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun endAllFiberSessions(fiber: FlowStateMachineImpl<*>, result: Try<*>, propagated: Boolean) {
|
||||
openSessions.values.removeIf { session ->
|
||||
if (session.fiber == fiber) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.node.services.events
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowLogicRef
|
||||
@ -136,6 +137,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
}
|
||||
|
||||
class TestFlowLogic(val increment: Int = 1) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
(serviceHub as TestReference).testReference.calls += increment
|
||||
(serviceHub as TestReference).testReference.countDown.countDown()
|
||||
|
@ -913,6 +913,7 @@ class FlowFrameworkTests {
|
||||
override val progressTracker: ProgressTracker = ProgressTracker(START_STEP)
|
||||
lateinit var exceptionThrown: E
|
||||
|
||||
@Suspendable
|
||||
override fun call(): Nothing {
|
||||
progressTracker.currentStep = START_STEP
|
||||
exceptionThrown = exception()
|
||||
|
@ -23,6 +23,7 @@ object UpdateBusinessDayFlow {
|
||||
|
||||
@InitiatedBy(Broadcast::class)
|
||||
private class UpdateBusinessDayHandler(val otherParty: Party) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val message = receive<UpdateBusinessDayMessage>(otherParty).unwrap { it }
|
||||
(serviceHub.clock as TestClock).updateDate(message.date)
|
||||
|
Loading…
x
Reference in New Issue
Block a user