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:
Katelyn Baker
2018-07-27 10:56:17 +01:00
committed by GitHub
parent c8de5ce08d
commit e871b83464
25 changed files with 435 additions and 130 deletions

View File

@ -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");
});
}

View File

@ -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()
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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