Including FlowException in the RPC exception whitelist (CORDA-1264) (#3037)

These exceptions are designed to be propagated in P2P and so makes sense to keep them visible if the recipient is an RPC user.
This commit is contained in:
Shams Asari
2018-05-01 07:48:50 +01:00
committed by GitHub
parent 42edf58b92
commit adef57f127
3 changed files with 43 additions and 45 deletions

View File

@ -29,61 +29,70 @@ class RpcExceptionHandlingTest {
@Test
fun `rpc client handles exceptions thrown on node side`() {
driver(DriverParameters(startNodesInProcess = true)) {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
val node = startNode(NodeParameters(rpcUsers = users)).getOrThrow()
assertThatCode { node.rpc.startFlow(::Flow).returnValue.getOrThrow() }.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
}
assertThatCode { node.rpc.startFlow(::Flow).returnValue.getOrThrow() }
.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
}
}
}
@Test
fun `rpc client handles client-relevant exceptions thrown on node side`() {
driver(DriverParameters(startNodesInProcess = true)) {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
val node = startNode(NodeParameters(rpcUsers = users)).getOrThrow()
val clientRelevantMessage = "This is for the players!"
assertThatCode { node.rpc.startFlow(::ClientRelevantErrorFlow, clientRelevantMessage).returnValue.getOrThrow() }.isInstanceOfSatisfying(ClientRelevantException::class.java) { exception ->
assertThatCode { node.rpc.startFlow(::ClientRelevantErrorFlow, clientRelevantMessage).returnValue.getOrThrow() }
.isInstanceOfSatisfying(ClientRelevantException::class.java) { exception ->
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(clientRelevantMessage)
}
}
}
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(clientRelevantMessage)
}
@Test
fun `FlowException is received by the RPC client`() {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
val node = startNode(NodeParameters(rpcUsers = users)).getOrThrow()
val exceptionMessage = "Flow error!"
assertThatCode { node.rpc.startFlow(::FlowExceptionFlow, exceptionMessage).returnValue.getOrThrow() }
.isInstanceOfSatisfying(FlowException::class.java) { exception ->
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(exceptionMessage)
}
}
}
@Test
fun `rpc client handles exceptions thrown on counter-party side`() {
driver(DriverParameters(startNodesInProcess = true)) {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
val nodeA = startNode(NodeParameters(providedName = ALICE_NAME, rpcUsers = users)).getOrThrow()
val nodeB = startNode(NodeParameters(providedName = BOB_NAME, rpcUsers = users)).getOrThrow()
assertThatCode { nodeA.rpc.startFlow(::InitFlow, nodeB.nodeInfo.singleIdentity()).returnValue.getOrThrow() }.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
}
assertThatCode { nodeA.rpc.startFlow(::InitFlow, nodeB.nodeInfo.singleIdentity()).returnValue.getOrThrow() }
.isInstanceOfSatisfying(InternalNodeException::class.java) { exception ->
assertThat(exception).hasNoCause()
assertThat(exception.stackTrace).isEmpty()
assertThat(exception.message).isEqualTo(InternalNodeException.defaultMessage())
}
}
}
}
@StartableByRPC
class Flow : FlowLogic<String>() {
@Suspendable
override fun call(): String {
throw GenericJDBCException("Something went wrong!", SQLException("Oops!"))
}
}
@ -91,10 +100,8 @@ class Flow : FlowLogic<String>() {
@StartableByRPC
@InitiatingFlow
class InitFlow(private val party: Party) : FlowLogic<String>() {
@Suspendable
override fun call(): String {
val session = initiateFlow(party)
return session.sendAndReceive<String>("hey").unwrap { it }
}
@ -102,10 +109,8 @@ class InitFlow(private val party: Party) : FlowLogic<String>() {
@InitiatedBy(InitFlow::class)
class InitiatedFlow(private val initiatingSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
initiatingSession.receive<String>().unwrap { it }
throw GenericJDBCException("Something went wrong!", SQLException("Oops!"))
}
@ -113,10 +118,12 @@ class InitiatedFlow(private val initiatingSession: FlowSession) : FlowLogic<Unit
@StartableByRPC
class ClientRelevantErrorFlow(private val message: String) : FlowLogic<String>() {
@Suspendable
override fun call(): String {
override fun call(): String = throw ClientRelevantException(message, SQLException("Oops!"))
}
throw ClientRelevantException(message, SQLException("Oops!"))
}
}
@StartableByRPC
class FlowExceptionFlow(private val message: String) : FlowLogic<String>() {
@Suspendable
override fun call(): String = throw FlowException(message)
}