Better exception handling wrt RMI method invocation
This commit is contained in:
parent
994fa8d196
commit
e621bfb4e3
@ -31,36 +31,25 @@ internal class ListenerManager<CONNECTION: Connection>(private val logger: KLogg
|
||||
*/
|
||||
fun cleanStackTrace(throwable: Throwable) {
|
||||
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
|
||||
val reversedList = throwable.stackTrace.reversed().toMutableList()
|
||||
|
||||
// we have to remove kotlin stuff from the stacktrace
|
||||
val reverseIter = reversedList.iterator()
|
||||
while (reverseIter.hasNext()) {
|
||||
val stackName = reverseIter.next().className
|
||||
if (stackName.startsWith("kotlinx.coroutines") || stackName.startsWith("kotlin.coroutines")) {
|
||||
// cleanup the stack elements which create the stacktrace
|
||||
reverseIter.remove()
|
||||
} else {
|
||||
// done cleaning up the tail from kotlin
|
||||
break
|
||||
val stackTrace = throwable.stackTrace
|
||||
var newEndIndex = stackTrace.size - 1
|
||||
for (i in newEndIndex downTo 0) {
|
||||
val stackName = stackTrace[i].className
|
||||
if (i == newEndIndex) {
|
||||
if (stackName.startsWith("kotlinx.coroutines.") ||
|
||||
stackName.startsWith("kotlin.coroutines.") ||
|
||||
stackName.startsWith("dorkbox.network.")) {
|
||||
newEndIndex--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove dorkbox network stuff
|
||||
while (reverseIter.hasNext()) {
|
||||
val stackName = reverseIter.next().className
|
||||
if (stackName.startsWith("dorkbox.network")) {
|
||||
// cleanup the stack elements which create the stacktrace
|
||||
reverseIter.remove()
|
||||
} else {
|
||||
// done cleaning up the tail from network
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
throwable.stackTrace = reversedList.reversed().toTypedArray()
|
||||
// tailToChopIndex will also remove the VERY LAST CachedMethod or CachedAsmMethod access invocation (because it's offset by 1)
|
||||
// NOTE: we want to do this!
|
||||
throwable.stackTrace = stackTrace.copyOfRange(0, newEndIndex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// initialize a emtpy arrays
|
||||
|
@ -78,7 +78,6 @@ internal class RmiClient(val isGlobal: Boolean,
|
||||
// if we are ASYNC, then this method immediately returns
|
||||
private suspend fun sendRequest(method: Method, args: Array<Any>): Any? {
|
||||
|
||||
|
||||
// there is a STRANGE problem, where if we DO NOT respond/reply to method invocation, and immediate invoke multiple methods --
|
||||
// the "server" side can have out-of-order method invocation. There are 2 ways to solve this
|
||||
// 1) make the "server" side single threaded
|
||||
@ -114,6 +113,28 @@ internal class RmiClient(val isGlobal: Boolean,
|
||||
throw TimeoutException("Response timed out: ${method.declaringClass.name}.${method.name}")
|
||||
}
|
||||
is Exception -> {
|
||||
// reconstruct the stack trace, so the calling method knows where the method invocation happened, and can trace the call
|
||||
// this stack will ALWAYS run up to this method (so we remove from the top->down, to get to the call site)
|
||||
|
||||
val stackTrace = Exception().stackTrace
|
||||
val myClassName = RmiClient::class.java.name
|
||||
|
||||
var newStartIndex = 0
|
||||
for (element in stackTrace) {
|
||||
newStartIndex++
|
||||
|
||||
if (element.className == myClassName && element.methodName == "invoke") {
|
||||
// we do this 1 more time, because we want to remove the proxy invocation off the stack as well.
|
||||
newStartIndex++
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
val newStack = Array<StackTraceElement>(result.stackTrace.size + stackTrace.size - newStartIndex) { stackTrace[0] }
|
||||
result.stackTrace.copyInto(newStack)
|
||||
stackTrace.copyInto(newStack, result.stackTrace.size, newStartIndex)
|
||||
|
||||
result.stackTrace = newStack
|
||||
throw result
|
||||
}
|
||||
else -> {
|
||||
@ -252,6 +273,15 @@ internal class RmiClient(val isGlobal: Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
// trampoline so we can access suspend functions correctly and (if suspend) get the coroutine connection parameter)
|
||||
private fun invokeSuspendFunction(continuation: Continuation<*>, suspendFunction: suspend () -> Any?): Any? {
|
||||
return try {
|
||||
SuspendFunctionAccess.invokeSuspendFunction(suspendFunction, continuation)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.cause!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val prime = 31
|
||||
var result = 1
|
||||
@ -276,12 +306,4 @@ internal class RmiClient(val isGlobal: Boolean,
|
||||
|
||||
return rmiObjectId == other.rmiObjectId
|
||||
}
|
||||
|
||||
private fun invokeSuspendFunction(continuation: Continuation<*>, suspendFunction: suspend () -> Any?): Any? {
|
||||
return try {
|
||||
SuspendFunctionAccess.invokeSuspendFunction(suspendFunction, continuation)
|
||||
} catch (e: InvocationTargetException) {
|
||||
throw e.cause!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -358,11 +358,7 @@ internal class RmiSupport(logger: KLogger,
|
||||
result.initCause(null)
|
||||
}
|
||||
|
||||
// only remove stuff if our logger is NOT on trace (so normal logs will not show extra info, trace will show extra info)
|
||||
if (!logger.isTraceEnabled) {
|
||||
ListenerManager.cleanStackTrace(result as Throwable)
|
||||
}
|
||||
|
||||
ListenerManager.cleanStackTrace(result as Throwable)
|
||||
logger.error("Error invoking method: ${cachedMethod.method.declaringClass.name}.${cachedMethod.method.name}", result)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user