From d1891faa4d0f6a5149b5d6e88380d4bed9cf5a17 Mon Sep 17 00:00:00 2001 From: mkit Date: Wed, 27 Sep 2017 13:17:55 +0100 Subject: [PATCH] FlowSnapshot serialization error (#1671) * Converting SubList to list due to the Kryo lack of support * Adding test * Addressing review comments --- .../corda/testing/FlowStackSnapshotTest.kt | 55 +++++++++++++++++-- .../net/corda/testing/FlowStackSnapshot.kt | 3 +- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt index 0aa4bec5e4..33d682f2b4 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt @@ -2,23 +2,25 @@ package net.corda.testing import co.paralleluniverse.fibers.Suspendable import net.corda.client.jackson.JacksonSupport -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.FlowStackSnapshot -import net.corda.core.flows.StartableByRPC -import net.corda.core.flows.StateMachineRunId +import net.corda.core.flows.* import net.corda.core.internal.div import net.corda.core.internal.list import net.corda.core.internal.read import net.corda.core.messaging.startFlow import net.corda.core.serialization.CordaSerializable import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.network.NetworkMapService +import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.driver.driver +import net.corda.testing.node.MockNetwork import org.junit.Ignore import org.junit.Test import java.nio.file.Path import java.time.LocalDate import kotlin.test.assertEquals +import kotlin.test.assertNull import kotlin.test.assertTrue @CordaSerializable @@ -178,6 +180,31 @@ class MultiplePersistingSideEffectFlow(val persistCallCount: Int) : FlowLogic() { + + @Suspendable + override fun call() { + val flowStackSnapshot = flowStackSnapshot() + val mySession = initiateFlow(ourIdentity) + mySession.sendAndReceive("Ping") + } +} + +@InitiatedBy(FlowStackSnapshotSerializationTestingFlow::class) +class DummyFlow(private val otherSideSession: FlowSession) : FlowLogic() { + + @Suspendable + override fun call() { + val message = otherSideSession.receive() + otherSideSession.send("$message Pong") + } +} + fun readFlowStackSnapshotFromDir(baseDir: Path, flowId: StateMachineRunId): FlowStackSnapshot { val snapshotFile = flowSnapshotDir(baseDir, flowId) / "flowStackSnapshot.json" return snapshotFile.read { @@ -263,6 +290,26 @@ class FlowStackSnapshotTest { } } + @Test + fun `flowStackSnapshot object is serializable`() { + val mockNet = MockNetwork(threadPerNode = true) + val notaryService = ServiceInfo(ValidatingNotaryService.type) + val notaryNode = mockNet.createNode( + legalName = DUMMY_NOTARY.name, + overrideServices = mapOf(notaryService to DUMMY_NOTARY_KEY), + advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), notaryService)) + val node = mockNet.createPartyNode(notaryNode.network.myAddress) + node.internals.registerInitiatedFlow(DummyFlow::class.java) + node.services.startFlow(FlowStackSnapshotSerializationTestingFlow()).resultFuture.get() + val thrown = try { + mockNet.stopNodes() + null + } catch (exception: Exception) { + exception + } + assertNull(thrown) + } + @Test fun `persistFlowStackSnapshot stack traces are aligned with stack objects`() { driver(startNodesInProcess = true) { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt index d51d5e7fe5..d79568d693 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt @@ -55,7 +55,8 @@ class FlowStackSnapshotFactoryImpl : FlowStackSnapshotFactory { val objectStack = getObjectStack(stack).toList() val frameOffsets = getFrameOffsets(stack) val frameObjects = frameOffsets.map { (frameOffset, frameSize) -> - objectStack.subList(frameOffset + 1, frameOffset + frameSize + 1) + // We need to convert the sublist to a list due to the Kryo lack of support when serializing + objectStack.subList(frameOffset + 1, frameOffset + frameSize + 1).toList() } // We drop the first element as it is corda internal call irrelevant from the perspective of a CordApp developer val relevantStackTrace = removeConstructorStackTraceElements(stackTrace).drop(1)