mirror of
https://github.com/corda/corda.git
synced 2025-06-23 01:19:00 +00:00
CORDA-1672 - Enable better user-helpful error messages (#3445)
* CORDA-1672 - Enable better user-helpful error messages The issue is that the error reporting framework in the serializer is targeted at developers in the node. However, because we pass exceptions to users over RPC those error messages aren't always helpful. Keep an internal exception that tracks debug useful information and log that just before any exception escapes the framework and allow for specific user "problem mitigation" issues to be set. * wip * update remaining excepions
This commit is contained in:
@ -4,6 +4,7 @@ import com.google.common.collect.Maps;
|
||||
import net.corda.core.serialization.SerializationContext;
|
||||
import net.corda.core.serialization.SerializationFactory;
|
||||
import net.corda.core.serialization.SerializedBytes;
|
||||
import net.corda.serialization.internal.amqp.AMQPNotSerializableException;
|
||||
import net.corda.serialization.internal.amqp.SchemaKt;
|
||||
import net.corda.testing.core.SerializationEnvironmentRule;
|
||||
import org.junit.Before;
|
||||
@ -43,7 +44,7 @@ public final class ForbiddenLambdaSerializationTests {
|
||||
assertThat(throwable)
|
||||
.isNotNull()
|
||||
.isInstanceOf(NotSerializableException.class)
|
||||
.hasMessageContaining(getClass().getName());
|
||||
.hasMessageContaining("Serializer does not support synthetic classes");
|
||||
});
|
||||
}
|
||||
|
||||
@ -61,7 +62,7 @@ public final class ForbiddenLambdaSerializationTests {
|
||||
assertThat(throwable)
|
||||
.isNotNull()
|
||||
.isInstanceOf(NotSerializableException.class)
|
||||
.hasMessageContaining(getClass().getName());
|
||||
.hasMessageContaining("Serializer does not support synthetic classes");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,130 @@
|
||||
package net.corda.serialization.internal.amqp
|
||||
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.Test
|
||||
import java.io.NotSerializableException
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AMQPExceptionsTests {
|
||||
|
||||
interface Runner {
|
||||
fun run()
|
||||
}
|
||||
|
||||
data class A<T : Runner>(val a: T, val throws: Boolean = false) : Runner {
|
||||
override fun run() = ifThrowsAppend({ javaClass.name }) {
|
||||
if (throws) {
|
||||
throw AMQPNotSerializableException(A::class.java, "it went bang!")
|
||||
} else {
|
||||
a.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class B<T : Runner>(val b: T, val throws: Boolean = false) : Runner {
|
||||
override fun run() = ifThrowsAppend({ javaClass.name }) {
|
||||
if (throws) {
|
||||
throw NotSerializableException(javaClass.name)
|
||||
} else {
|
||||
b.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class C<T : Runner>(val c: T, val throws: Boolean = false) : Runner {
|
||||
override fun run() = ifThrowsAppend({ javaClass.name }) {
|
||||
if (throws) {
|
||||
throw NotSerializableException(javaClass.name)
|
||||
} else {
|
||||
c.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class END(val throws: Boolean = false) : Runner {
|
||||
override fun run() = ifThrowsAppend({ "END" }) {
|
||||
if (throws) {
|
||||
throw NotSerializableException(javaClass.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ENDAMQP(val throws: Boolean = false) : Runner {
|
||||
override fun run() {
|
||||
if (throws) {
|
||||
throw AMQPNotSerializableException(javaClass, "End it all")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val aName get() = A::class.java.name
|
||||
private val bName get() = B::class.java.name
|
||||
private val cName get() = C::class.java.name
|
||||
private val eName get() = END::class.java.name
|
||||
private val eaName get() = ENDAMQP::class.java.name
|
||||
|
||||
// if the exception is a normal not serializable exception we'll have manipulated the
|
||||
// message
|
||||
@Test
|
||||
fun catchNotSerializable() {
|
||||
fun catchAssert(msg: String, f: () -> Unit) {
|
||||
Assertions.assertThatThrownBy { f() }
|
||||
.isInstanceOf(NotSerializableException::class.java)
|
||||
.hasMessageContaining(msg)
|
||||
}
|
||||
|
||||
catchAssert("$aName -> END") {
|
||||
A(END(true)).run()
|
||||
}
|
||||
|
||||
catchAssert("$aName -> $aName -> END") {
|
||||
A(A(END(true))).run()
|
||||
}
|
||||
|
||||
catchAssert("$aName -> $bName -> END") {
|
||||
A(B(END(true))).run()
|
||||
}
|
||||
|
||||
catchAssert("$aName -> $bName -> $cName -> END") {
|
||||
A(B(C(END(true)))).run()
|
||||
}
|
||||
}
|
||||
|
||||
// However, if its a shiny new AMQPNotSerializable one, we have cool new toys, so
|
||||
// lets make sure those are set
|
||||
@Test
|
||||
fun catchAMQPNotSerializable() {
|
||||
fun catchAssert(stack: List<String>, f: () -> Unit): AMQPNotSerializableException {
|
||||
try {
|
||||
f()
|
||||
} catch (e: AMQPNotSerializableException) {
|
||||
assertEquals(stack, e.classHierarchy)
|
||||
return e
|
||||
}
|
||||
|
||||
throw Exception("FAILED")
|
||||
}
|
||||
|
||||
|
||||
catchAssert(listOf(ENDAMQP::class.java.name, aName)) {
|
||||
A(ENDAMQP(true)).run()
|
||||
}.apply {
|
||||
assertEquals(
|
||||
"Serialization failed direction=\"up\", type=\"$eaName\", msg=\"End it all\", " +
|
||||
"ClassChain=\"$aName -> $eaName\"",
|
||||
errorMessage("up"))
|
||||
}
|
||||
|
||||
catchAssert(listOf(ENDAMQP::class.java.name, aName, aName)) {
|
||||
A(A(ENDAMQP(true))).run()
|
||||
}
|
||||
|
||||
catchAssert(listOf(ENDAMQP::class.java.name, bName, aName)) {
|
||||
A(B(ENDAMQP(true))).run()
|
||||
}
|
||||
|
||||
catchAssert(listOf(ENDAMQP::class.java.name, cName, bName, aName)) {
|
||||
A(B(C(ENDAMQP(true)))).run()
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ import net.corda.serialization.internal.amqp.testutils.*
|
||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.assertj.core.api.Condition
|
||||
import org.junit.Test
|
||||
import java.io.NotSerializableException
|
||||
import java.net.URI
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput
|
||||
import net.corda.serialization.internal.amqp.testutils.deserialize
|
||||
import net.corda.serialization.internal.amqp.testutils.deserializeAndReturnEnvelope
|
||||
import net.corda.serialization.internal.amqp.testutils.serialize
|
||||
import net.corda.serialization.internal.amqp.testutils.serializeAndReturnSchema
|
||||
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
|
||||
import net.corda.serialization.internal.amqp.testutils.testName
|
||||
|
@ -123,7 +123,7 @@ class PrivatePropertyTests {
|
||||
SerializationOutput(factory).serialize(c1)
|
||||
}.isInstanceOf(NotSerializableException::class.java).hasMessageContaining(
|
||||
"Defined setter for parameter a takes parameter of type class java.lang.String " +
|
||||
"yet underlying type is int ")
|
||||
"yet underlying type is int")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user