mirror of
https://github.com/corda/corda.git
synced 2025-02-19 17:08:05 +00:00
CORDA-1610: In addition to injecting the old progress tracker after flow retry, attach and re-subscribe to child trackers as well. (#3440)
This commit is contained in:
parent
ad2890193d
commit
be1aff20a6
@ -3,20 +3,73 @@ package net.corda.node.utilities
|
|||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.node.services.statemachine.StateMachineManagerInternal
|
import net.corda.node.services.statemachine.StateMachineManagerInternal
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The flow de-serialized from the checkpoint will contain a new instance of the progress tracker, which means that
|
* The flow de-serialized from the checkpoint will contain a new instance of the progress tracker, which means that
|
||||||
* any existing flow observers would be lost. We need to replace it with the old progress tracker to ensure progress
|
* any existing flow observers would be lost. We need to replace it with the old progress tracker to ensure progress
|
||||||
* updates are correctly sent out after the flow is retried.
|
* updates are correctly sent out after the flow is retried.
|
||||||
|
*
|
||||||
|
* If the new tracker contains any child trackers from sub-flows, we need to attach those to the old tracker as well.
|
||||||
*/
|
*/
|
||||||
fun StateMachineManagerInternal.injectOldProgressTracker(oldProgressTracker: ProgressTracker?, newFlowLogic: FlowLogic<*>) {
|
//TODO: instead of replacing the progress tracker after constructing the flow logic, we should inject it during fiber deserialization
|
||||||
if (oldProgressTracker != null) {
|
fun StateMachineManagerInternal.injectOldProgressTracker(oldTracker: ProgressTracker?, newFlowLogic: FlowLogic<*>) {
|
||||||
try {
|
if (oldTracker != null) {
|
||||||
val field = newFlowLogic::class.java.getDeclaredField("progressTracker")
|
val newTracker = newFlowLogic.progressTracker
|
||||||
field.isAccessible = true
|
if (newTracker != null) {
|
||||||
field.set(newFlowLogic, oldProgressTracker)
|
attachNewChildren(oldTracker, newTracker)
|
||||||
} catch (e: NoSuchFieldException) {
|
replaceTracker(newFlowLogic, oldTracker)
|
||||||
// The flow does not use a progress tracker.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun attachNewChildren(oldTracker: ProgressTracker, newTracker: ProgressTracker) {
|
||||||
|
oldTracker.currentStep = newTracker.currentStep
|
||||||
|
oldTracker.steps.forEachIndexed { index, step ->
|
||||||
|
val newStep = newTracker.steps[index]
|
||||||
|
val newChildTracker = newTracker.getChildProgressTracker(newStep)
|
||||||
|
newChildTracker?.let { child ->
|
||||||
|
oldTracker.setChildProgressTracker(step, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resubscribeToChildren(oldTracker)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-subscribes to child tracker observables. When a nested progress tracker is deserialized from a checkpoint,
|
||||||
|
* it retains the child links, but does not automatically re-subscribe to the child changes.
|
||||||
|
*/
|
||||||
|
private fun resubscribeToChildren(tracker: ProgressTracker) {
|
||||||
|
tracker.steps.forEach {
|
||||||
|
val childTracker = tracker.getChildProgressTracker(it)
|
||||||
|
if (childTracker != null) {
|
||||||
|
tracker.setChildProgressTracker(it, childTracker)
|
||||||
|
resubscribeToChildren(childTracker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replaces the deserialized [ProgressTracker] in the [newFlowLogic] with the old one to retain old subscribers. */
|
||||||
|
private fun replaceTracker(newFlowLogic: FlowLogic<*>, oldProgressTracker: ProgressTracker?) {
|
||||||
|
val field = getProgressTrackerField(newFlowLogic)
|
||||||
|
field?.apply {
|
||||||
|
isAccessible = true
|
||||||
|
set(newFlowLogic, oldProgressTracker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProgressTrackerField(newFlowLogic: FlowLogic<*>): Field? {
|
||||||
|
var clazz: Class<*> = newFlowLogic::class.java
|
||||||
|
var field: Field? = null
|
||||||
|
// The progress tracker field may have been overridden in an abstract superclass, so we have to traverse up
|
||||||
|
// the hierarchy.
|
||||||
|
while (clazz != FlowLogic::class.java) {
|
||||||
|
field = clazz.declaredFields.firstOrNull { it.name == "progressTracker" }
|
||||||
|
if (field == null) {
|
||||||
|
clazz = clazz.superclass
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user