Work on cleaning up how exceptions are thrown/logged
This commit is contained in:
parent
389af93f0a
commit
59773b4cce
|
@ -93,7 +93,7 @@ open class Client<CONNECTION : Connection>(config: Configuration = Configuration
|
||||||
/**
|
/**
|
||||||
* So the client class can get remote objects that are THE SAME OBJECT as if called from a connection
|
* So the client class can get remote objects that are THE SAME OBJECT as if called from a connection
|
||||||
*/
|
*/
|
||||||
override fun getRmiConnectionSupport(): RmiManagerConnections {
|
override fun getRmiConnectionSupport(): RmiManagerConnections<CONNECTION> {
|
||||||
return rmiConnectionSupport
|
return rmiConnectionSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.network.connection
|
package dorkbox.network.connection
|
||||||
|
|
||||||
import dorkbox.network.Configuration
|
|
||||||
import dorkbox.util.collections.ConcurrentEntry
|
import dorkbox.util.collections.ConcurrentEntry
|
||||||
import dorkbox.util.collections.ConcurrentIterator
|
import dorkbox.util.collections.ConcurrentIterator
|
||||||
import dorkbox.util.collections.ConcurrentIterator.headREF
|
import dorkbox.util.collections.ConcurrentIterator.headREF
|
||||||
import mu.KLogger
|
|
||||||
|
|
||||||
// .equals() compares the identity on purpose,this because we cannot create two separate objects that are somehow equal to each other.
|
// .equals() compares the identity on purpose,this because we cannot create two separate objects that are somehow equal to each other.
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
internal open class ConnectionManager<CONNECTION: Connection>(val logger: KLogger, val config: Configuration) {
|
internal open class ConnectionManager<CONNECTION: Connection>() {
|
||||||
|
|
||||||
private val connections = ConcurrentIterator<CONNECTION>()
|
private val connections = ConcurrentIterator<CONNECTION>()
|
||||||
|
|
||||||
|
|
|
@ -15,5 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.network.connection
|
package dorkbox.network.connection
|
||||||
|
|
||||||
data class ConnectionParams<C: Connection>(val endPoint: EndPoint<C>, val mediaDriverConnection: MediaDriverConnection,
|
data class ConnectionParams<C : Connection>(val endPoint: EndPoint<C>,
|
||||||
val publicKeyValidation: PublicKeyValidationState)
|
val mediaDriverConnection: MediaDriverConnection,
|
||||||
|
val publicKeyValidation: PublicKeyValidationState)
|
||||||
|
|
|
@ -106,10 +106,9 @@ internal constructor(val type: Class<*>, internal val config: Configuration) : A
|
||||||
internal val autoClosableObjects = CopyOnWriteArrayList<AutoCloseable>()
|
internal val autoClosableObjects = CopyOnWriteArrayList<AutoCloseable>()
|
||||||
|
|
||||||
internal val actionDispatch = CoroutineScope(Dispatchers.Default)
|
internal val actionDispatch = CoroutineScope(Dispatchers.Default)
|
||||||
// internal val connectionActor = actionDispatch.connectionActor()
|
|
||||||
|
|
||||||
internal val listenerManager = ListenerManager<CONNECTION>()
|
internal val listenerManager = ListenerManager<CONNECTION>()
|
||||||
internal val connections = ConnectionManager<CONNECTION>(logger, config)
|
internal val connections = ConnectionManager<CONNECTION>()
|
||||||
|
|
||||||
internal val mediaDriverContext: MediaDriver.Context
|
internal val mediaDriverContext: MediaDriver.Context
|
||||||
internal val mediaDriver: MediaDriver
|
internal val mediaDriver: MediaDriver
|
||||||
|
@ -133,7 +132,7 @@ internal constructor(val type: Class<*>, internal val config: Configuration) : A
|
||||||
// we only want one instance of these created. These will be called appropriately
|
// we only want one instance of these created. These will be called appropriately
|
||||||
val settingsStore: SettingsStore
|
val settingsStore: SettingsStore
|
||||||
|
|
||||||
internal val rmiGlobalSupport = RmiManagerGlobal(logger, actionDispatch, config.serialization)
|
internal val rmiGlobalSupport = RmiManagerGlobal<CONNECTION>(logger, actionDispatch, config.serialization)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
logger.error("NETWORK STACK IS ONLY IPV4 AT THE MOMENT. IPV6 is in progress!")
|
logger.error("NETWORK STACK IS ONLY IPV4 AT THE MOMENT. IPV6 is in progress!")
|
||||||
|
@ -318,7 +317,7 @@ internal constructor(val type: Class<*>, internal val config: Configuration) : A
|
||||||
* Used for the client, because the client only has ONE ever support connection, and it allows us to create connection specific objects
|
* Used for the client, because the client only has ONE ever support connection, and it allows us to create connection specific objects
|
||||||
* from a "global" context
|
* from a "global" context
|
||||||
*/
|
*/
|
||||||
internal open fun getRmiConnectionSupport() : RmiManagerConnections {
|
internal open fun getRmiConnectionSupport() : RmiManagerConnections<CONNECTION> {
|
||||||
return RmiManagerConnections(logger, rmiGlobalSupport, serialization)
|
return RmiManagerConnections(logger, rmiGlobalSupport, serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +511,8 @@ internal constructor(val type: Class<*>, internal val config: Configuration) : A
|
||||||
serialization.returnKryo(kryo)
|
serialization.returnKryo(kryo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connection as CONNECTION
|
||||||
|
|
||||||
when (message) {
|
when (message) {
|
||||||
is PingMessage -> {
|
is PingMessage -> {
|
||||||
// the ping listener (internal use only!)
|
// the ping listener (internal use only!)
|
||||||
|
@ -532,17 +533,27 @@ internal constructor(val type: Class<*>, internal val config: Configuration) : A
|
||||||
}
|
}
|
||||||
is Any -> {
|
is Any -> {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
var hasListeners = listenerManager.notifyOnMessage(connection as CONNECTION, message)
|
var hasListeners = listenerManager.notifyOnMessage(connection, message)
|
||||||
|
|
||||||
// each connection registers, and is polled INDEPENDENTLY for messages.
|
// each connection registers, and is polled INDEPENDENTLY for messages.
|
||||||
hasListeners = hasListeners or connection.notifyOnMessage(message)
|
hasListeners = hasListeners or connection.notifyOnMessage(message)
|
||||||
|
|
||||||
if (!hasListeners) {
|
if (!hasListeners) {
|
||||||
listenerManager.notifyError(connection, MessageNotRegisteredException("No message callbacks found for ${message::class.java.simpleName}"))
|
val exception = MessageNotRegisteredException("No message callbacks found for ${message::class.java.simpleName}")
|
||||||
|
ListenerManager.cleanStackTrace(exception)
|
||||||
|
listenerManager.notifyError(connection, exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// do nothing, there were problems with the message
|
// do nothing, there were problems with the message
|
||||||
|
val exception = if (message != null) {
|
||||||
|
MessageNotRegisteredException("No message callbacks found for ${message::class.java.simpleName}")
|
||||||
|
} else {
|
||||||
|
MessageNotRegisteredException("Unknown message received!!")
|
||||||
|
}
|
||||||
|
|
||||||
|
ListenerManager.cleanStackTrace(exception)
|
||||||
|
listenerManager.notifyError(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,27 +42,32 @@ internal class ListenerManager<CONNECTION: Connection> {
|
||||||
*
|
*
|
||||||
* Neither of these are useful in resolving exception handling from a users perspective, and only clutter the stacktrace.
|
* Neither of these are useful in resolving exception handling from a users perspective, and only clutter the stacktrace.
|
||||||
*/
|
*/
|
||||||
fun cleanStackTraceReverse(throwable: Throwable) {
|
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
|
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
|
||||||
val stackTrace = throwable.stackTrace
|
val stackTrace = throwable.stackTrace
|
||||||
var newEndIndex = Math.max(0, stackTrace.size - 1)
|
var newEndIndex = stackTrace.size -1 // offset by 1 because we have to adjust for the access index
|
||||||
|
|
||||||
if (newEndIndex > 0) {
|
for (i in newEndIndex downTo 0) {
|
||||||
for (i in newEndIndex downTo 0) {
|
val stackName = stackTrace[i].className
|
||||||
val stackName = stackTrace[i].className
|
if (i == newEndIndex) {
|
||||||
if (i == newEndIndex) {
|
if (stackName.startsWith("kotlinx.coroutines.") ||
|
||||||
if (stackName.startsWith("kotlinx.coroutines.") ||
|
stackName.startsWith("kotlin.coroutines.") ||
|
||||||
stackName.startsWith("kotlin.coroutines.") ||
|
stackName.startsWith("dorkbox.network.")) {
|
||||||
stackName.startsWith("dorkbox.network.")) {
|
newEndIndex--
|
||||||
newEndIndex--
|
} else {
|
||||||
} else {
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newEndIndex++ // have to add 1 back, because a copy must be by size (and we access from 0)
|
||||||
|
|
||||||
|
if (newEndIndex > 0) {
|
||||||
// newEndIndex will also remove the VERY LAST CachedMethod or CachedAsmMethod access invocation (because it's offset by 1)
|
// newEndIndex will also remove the VERY LAST CachedMethod or CachedAsmMethod access invocation (because it's offset by 1)
|
||||||
throwable.stackTrace = stackTrace.copyOfRange(0, newEndIndex)
|
throwable.stackTrace = stackTrace.copyOfRange(0, newEndIndex)
|
||||||
|
} else {
|
||||||
|
// keep just one, since it's a stack frame INSIDE our network library, and we need that!
|
||||||
|
throwable.stackTrace = stackTrace.copyOfRange(0, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +318,7 @@ internal class ListenerManager<CONNECTION: Connection> {
|
||||||
it(connection)
|
it(connection)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
|
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
|
||||||
cleanStackTraceReverse(t)
|
cleanStackTrace(t)
|
||||||
notifyError(connection, t)
|
notifyError(connection, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,7 +333,7 @@ internal class ListenerManager<CONNECTION: Connection> {
|
||||||
it(connection)
|
it(connection)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
|
// NOTE: when we remove stuff, we ONLY want to remove the "tail" of the stacktrace, not ALL parts of the stacktrace
|
||||||
cleanStackTraceReverse(t)
|
cleanStackTrace(t)
|
||||||
notifyError(connection, t)
|
notifyError(connection, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,7 +341,7 @@ internal class ListenerManager<CONNECTION: Connection> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when there is an error for a specific connection
|
* Invoked when there is an error for a specific connection
|
||||||
* <p>
|
*
|
||||||
* The error is also sent to an error log before notifying callbacks
|
* The error is also sent to an error log before notifying callbacks
|
||||||
*/
|
*/
|
||||||
suspend fun notifyError(connection: CONNECTION, exception: Throwable) {
|
suspend fun notifyError(connection: CONNECTION, exception: Throwable) {
|
||||||
|
@ -347,7 +352,7 @@ internal class ListenerManager<CONNECTION: Connection> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when there is an error in general
|
* Invoked when there is an error in general
|
||||||
* <p>
|
*
|
||||||
* The error is also sent to an error log before notifying callbacks
|
* The error is also sent to an error log before notifying callbacks
|
||||||
*/
|
*/
|
||||||
suspend fun notifyError(exception: Throwable) {
|
suspend fun notifyError(exception: Throwable) {
|
||||||
|
@ -392,6 +397,7 @@ internal class ListenerManager<CONNECTION: Connection> {
|
||||||
try {
|
try {
|
||||||
func(connection, message)
|
func(connection, message)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
|
cleanStackTrace(t)
|
||||||
notifyError(connection, t)
|
notifyError(connection, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,10 @@ internal class ClientHandshake<CONNECTION: Connection>(private val logger: KLogg
|
||||||
private var failed: Exception? = null
|
private var failed: Exception? = null
|
||||||
|
|
||||||
lateinit var handler: FragmentHandler
|
lateinit var handler: FragmentHandler
|
||||||
lateinit var endPoint: EndPoint<*>
|
lateinit var endPoint: EndPoint<CONNECTION>
|
||||||
var sessionId: Int = 0
|
var sessionId: Int = 0
|
||||||
|
|
||||||
fun init(endPoint: EndPoint<*>) {
|
fun init(endPoint: EndPoint<CONNECTION>) {
|
||||||
this.endPoint = endPoint
|
this.endPoint = endPoint
|
||||||
|
|
||||||
// now we have a bi-directional connection with the server on the handshake "socket".
|
// now we have a bi-directional connection with the server on the handshake "socket".
|
||||||
|
|
|
@ -215,8 +215,9 @@ internal class ServerHandshake<CONNECTION : Connection>(private val logger: KLog
|
||||||
|
|
||||||
logger.error("Error creating new connection")
|
logger.error("Error creating new connection")
|
||||||
|
|
||||||
listenerManager.notifyError(connection,
|
val exception = ClientRejectedException("Connection was not permitted!")
|
||||||
ClientRejectedException("Connection was not permitted!"))
|
ListenerManager.cleanStackTrace(exception)
|
||||||
|
listenerManager.notifyError(connection, exception)
|
||||||
|
|
||||||
endPoint.writeHandshakeMessage(handshakePublication,
|
endPoint.writeHandshakeMessage(handshakePublication,
|
||||||
HandshakeMessage.error("Connection was not permitted!"))
|
HandshakeMessage.error("Connection was not permitted!"))
|
||||||
|
|
|
@ -256,13 +256,13 @@ internal class RmiClient(val isGlobal: Boolean,
|
||||||
val fancyName = RmiUtils.makeFancyMethodName(method)
|
val fancyName = RmiUtils.makeFancyMethodName(method)
|
||||||
val exception = TimeoutException("Response timed out: $fancyName")
|
val exception = TimeoutException("Response timed out: $fancyName")
|
||||||
// from top down, clean up the coroutine stack
|
// from top down, clean up the coroutine stack
|
||||||
RmiUtils.cleanStackTraceForProxy(exception, RmiClient::class.java)
|
RmiUtils.cleanStackTraceForProxy(exception)
|
||||||
continuation.resumeWithException(exception)
|
continuation.resumeWithException(exception)
|
||||||
}
|
}
|
||||||
is Exception -> {
|
is Exception -> {
|
||||||
// reconstruct the stack trace, so the calling method knows where the method invocation happened, and can trace the call
|
// 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)
|
// this stack will ALWAYS run up to this method (so we remove from the top->down, to get to the call site)
|
||||||
RmiUtils.cleanStackTraceForProxy(Exception(), RmiClient::class.java, any)
|
RmiUtils.cleanStackTraceForProxy(Exception(), any)
|
||||||
continuation.resumeWithException(any)
|
continuation.resumeWithException(any)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -279,13 +279,13 @@ internal class RmiClient(val isGlobal: Boolean,
|
||||||
val fancyName = RmiUtils.makeFancyMethodName(method)
|
val fancyName = RmiUtils.makeFancyMethodName(method)
|
||||||
val exception = TimeoutException("Response timed out: $fancyName")
|
val exception = TimeoutException("Response timed out: $fancyName")
|
||||||
// from top down, clean up the coroutine stack
|
// from top down, clean up the coroutine stack
|
||||||
RmiUtils.cleanStackTraceForProxy(exception, RmiClient::class.java)
|
RmiUtils.cleanStackTraceForProxy(exception)
|
||||||
throw exception
|
throw exception
|
||||||
}
|
}
|
||||||
is Exception -> {
|
is Exception -> {
|
||||||
// reconstruct the stack trace, so the calling method knows where the method invocation happened, and can trace the call
|
// 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)
|
// this stack will ALWAYS run up to this method (so we remove from the top->down, to get to the call site)
|
||||||
RmiUtils.cleanStackTraceForProxy(Exception(), RmiClient::class.java, any)
|
RmiUtils.cleanStackTraceForProxy(Exception(), any)
|
||||||
throw any
|
throw any
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -17,14 +17,15 @@ package dorkbox.network.rmi
|
||||||
|
|
||||||
import dorkbox.network.connection.Connection
|
import dorkbox.network.connection.Connection
|
||||||
import dorkbox.network.connection.EndPoint
|
import dorkbox.network.connection.EndPoint
|
||||||
|
import dorkbox.network.connection.ListenerManager
|
||||||
import dorkbox.network.rmi.messages.ConnectionObjectCreateRequest
|
import dorkbox.network.rmi.messages.ConnectionObjectCreateRequest
|
||||||
import dorkbox.network.rmi.messages.ConnectionObjectCreateResponse
|
import dorkbox.network.rmi.messages.ConnectionObjectCreateResponse
|
||||||
import dorkbox.network.serialization.NetworkSerializationManager
|
import dorkbox.network.serialization.NetworkSerializationManager
|
||||||
import dorkbox.util.collections.LockFreeIntMap
|
import dorkbox.util.collections.LockFreeIntMap
|
||||||
import mu.KLogger
|
import mu.KLogger
|
||||||
|
|
||||||
internal class RmiManagerConnections(logger: KLogger,
|
internal class RmiManagerConnections<CONNECTION: Connection>(logger: KLogger,
|
||||||
val rmiGlobalSupport: RmiManagerGlobal,
|
val rmiGlobalSupport: RmiManagerGlobal<CONNECTION>,
|
||||||
private val serialization: NetworkSerializationManager) : RmiObjectCache(logger) {
|
private val serialization: NetworkSerializationManager) : RmiObjectCache(logger) {
|
||||||
|
|
||||||
// It is critical that all of the RMI proxy objects are unique, and are saved/cached PER CONNECTION. These cannot be shared between connections!
|
// It is critical that all of the RMI proxy objects are unique, and are saved/cached PER CONNECTION. These cannot be shared between connections!
|
||||||
|
@ -82,7 +83,7 @@ internal class RmiManagerConnections(logger: KLogger,
|
||||||
/**
|
/**
|
||||||
* called on "server"
|
* called on "server"
|
||||||
*/
|
*/
|
||||||
internal suspend fun onConnectionObjectCreateRequest(endPoint: EndPoint<*>, connection: Connection, message: ConnectionObjectCreateRequest, logger: KLogger) {
|
suspend fun onConnectionObjectCreateRequest(endPoint: EndPoint<CONNECTION>, connection: CONNECTION, message: ConnectionObjectCreateRequest) {
|
||||||
|
|
||||||
val interfaceClassId = RmiUtils.unpackLeft(message.packedIds)
|
val interfaceClassId = RmiUtils.unpackLeft(message.packedIds)
|
||||||
val callbackId = RmiUtils.unpackRight(message.packedIds)
|
val callbackId = RmiUtils.unpackRight(message.packedIds)
|
||||||
|
@ -93,7 +94,8 @@ internal class RmiManagerConnections(logger: KLogger,
|
||||||
|
|
||||||
val response = if (implObject is Exception) {
|
val response = if (implObject is Exception) {
|
||||||
// whoops!
|
// whoops!
|
||||||
logger.error("Unable to create remote object!", implObject)
|
ListenerManager.cleanStackTrace(implObject)
|
||||||
|
endPoint.listenerManager.notifyError(connection, implObject)
|
||||||
|
|
||||||
// we send the message ANYWAYS, because the client needs to know it did NOT succeed!
|
// we send the message ANYWAYS, because the client needs to know it did NOT succeed!
|
||||||
ConnectionObjectCreateResponse(RmiUtils.packShorts(callbackId, RemoteObjectStorage.INVALID_RMI))
|
ConnectionObjectCreateResponse(RmiUtils.packShorts(callbackId, RemoteObjectStorage.INVALID_RMI))
|
||||||
|
@ -104,13 +106,13 @@ internal class RmiManagerConnections(logger: KLogger,
|
||||||
// this means we could register this object.
|
// this means we could register this object.
|
||||||
|
|
||||||
// next, scan this object to see if there are any RMI fields
|
// next, scan this object to see if there are any RMI fields
|
||||||
RmiManagerGlobal.scanImplForRmiFields(logger, implObject) {
|
RmiManagerGlobal.scanImplForRmiFields(endPoint.logger, implObject) {
|
||||||
saveImplObject(it)
|
saveImplObject(it)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error {
|
val exception = NullPointerException("Trying to create an RMI object with the INVALID_RMI id!!")
|
||||||
"Trying to create an RMI object with the INVALID_RMI id!!"
|
ListenerManager.cleanStackTrace(exception)
|
||||||
}
|
endPoint.listenerManager.notifyError(connection, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we send the message ANYWAYS, because the client needs to know it did NOT succeed!
|
// we send the message ANYWAYS, because the client needs to know it did NOT succeed!
|
||||||
|
|
|
@ -17,6 +17,7 @@ package dorkbox.network.rmi
|
||||||
|
|
||||||
import dorkbox.network.connection.Connection
|
import dorkbox.network.connection.Connection
|
||||||
import dorkbox.network.connection.EndPoint
|
import dorkbox.network.connection.EndPoint
|
||||||
|
import dorkbox.network.connection.ListenerManager
|
||||||
import dorkbox.network.rmi.messages.ConnectionObjectCreateRequest
|
import dorkbox.network.rmi.messages.ConnectionObjectCreateRequest
|
||||||
import dorkbox.network.rmi.messages.ConnectionObjectCreateResponse
|
import dorkbox.network.rmi.messages.ConnectionObjectCreateResponse
|
||||||
import dorkbox.network.rmi.messages.GlobalObjectCreateRequest
|
import dorkbox.network.rmi.messages.GlobalObjectCreateRequest
|
||||||
|
@ -31,9 +32,9 @@ import mu.KLogger
|
||||||
import java.lang.reflect.Proxy
|
import java.lang.reflect.Proxy
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal class RmiManagerGlobal(logger: KLogger,
|
internal class RmiManagerGlobal<CONNECTION : Connection>(logger: KLogger,
|
||||||
actionDispatch: CoroutineScope,
|
actionDispatch: CoroutineScope,
|
||||||
internal val serialization: NetworkSerializationManager) : RmiObjectCache(logger) {
|
internal val serialization: NetworkSerializationManager) : RmiObjectCache(logger) {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Returns a proxy object that implements the specified interface, and the methods invoked on the proxy object will be invoked
|
* Returns a proxy object that implements the specified interface, and the methods invoked on the proxy object will be invoked
|
||||||
|
@ -214,15 +215,16 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
/**
|
/**
|
||||||
* called on "client"
|
* called on "client"
|
||||||
*/
|
*/
|
||||||
private fun onGenericObjectResponse(endPoint: EndPoint<*>, connection: Connection, logger: KLogger,
|
private suspend fun onGenericObjectResponse(endPoint: EndPoint<CONNECTION>,
|
||||||
isGlobal: Boolean, rmiId: Int, callback: suspend (Int, Any) -> Unit,
|
connection: CONNECTION,
|
||||||
rmiObjectCache: RmiObjectCache, serialization: NetworkSerializationManager) {
|
isGlobal: Boolean,
|
||||||
|
rmiId: Int,
|
||||||
|
callback: suspend (Int, Any) -> Unit,
|
||||||
|
serialization: NetworkSerializationManager) {
|
||||||
|
|
||||||
// we only create the proxy + execute the callback if the RMI id is valid!
|
// we only create the proxy + execute the callback if the RMI id is valid!
|
||||||
if (rmiId == RemoteObjectStorage.INVALID_RMI) {
|
if (rmiId == RemoteObjectStorage.INVALID_RMI) {
|
||||||
logger.error {
|
endPoint.listenerManager.notifyError(connection, Exception("RMI ID '${rmiId}' is invalid. Unable to create RMI object on server."))
|
||||||
"RMI ID '${rmiId}' is invalid. Unable to create RMI object on server."
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +242,8 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
try {
|
try {
|
||||||
callback(rmiId, proxyObject)
|
callback(rmiId, proxyObject)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Error getting or creating the remote object $interfaceClass", e)
|
ListenerManager.cleanStackTrace(e)
|
||||||
|
endPoint.listenerManager.notifyError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,13 +270,15 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
*/
|
*/
|
||||||
@Suppress("DuplicatedCode")
|
@Suppress("DuplicatedCode")
|
||||||
@Throws(IllegalArgumentException::class)
|
@Throws(IllegalArgumentException::class)
|
||||||
suspend fun manage(endPoint: EndPoint<*>, connection: Connection, message: Any, logger: KLogger) {
|
suspend fun manage(endPoint: EndPoint<CONNECTION>, connection: CONNECTION, message: Any, logger: KLogger) {
|
||||||
when (message) {
|
when (message) {
|
||||||
is ConnectionObjectCreateRequest -> {
|
is ConnectionObjectCreateRequest -> {
|
||||||
/**
|
/**
|
||||||
* called on "server"
|
* called on "server"
|
||||||
*/
|
*/
|
||||||
connection.rmiConnectionSupport.onConnectionObjectCreateRequest(endPoint, connection, message, logger)
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val rmiConnectionSupport: RmiManagerConnections<CONNECTION> = connection.rmiConnectionSupport as RmiManagerConnections<CONNECTION>
|
||||||
|
rmiConnectionSupport.onConnectionObjectCreateRequest(endPoint, connection, message)
|
||||||
}
|
}
|
||||||
is ConnectionObjectCreateResponse -> {
|
is ConnectionObjectCreateResponse -> {
|
||||||
/**
|
/**
|
||||||
|
@ -282,7 +287,7 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
val callbackId = RmiUtils.unpackLeft(message.packedIds)
|
val callbackId = RmiUtils.unpackLeft(message.packedIds)
|
||||||
val rmiId = RmiUtils.unpackRight(message.packedIds)
|
val rmiId = RmiUtils.unpackRight(message.packedIds)
|
||||||
val callback = removeCallback(callbackId)
|
val callback = removeCallback(callbackId)
|
||||||
onGenericObjectResponse(endPoint, connection, logger, false, rmiId, callback, this, serialization)
|
onGenericObjectResponse(endPoint, connection, false, rmiId, callback, serialization)
|
||||||
}
|
}
|
||||||
is GlobalObjectCreateRequest -> {
|
is GlobalObjectCreateRequest -> {
|
||||||
/**
|
/**
|
||||||
|
@ -297,7 +302,7 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
val callbackId = RmiUtils.unpackLeft(message.packedIds)
|
val callbackId = RmiUtils.unpackLeft(message.packedIds)
|
||||||
val rmiId = RmiUtils.unpackRight(message.packedIds)
|
val rmiId = RmiUtils.unpackRight(message.packedIds)
|
||||||
val callback = removeCallback(callbackId)
|
val callback = removeCallback(callbackId)
|
||||||
onGenericObjectResponse(endPoint, connection, logger, true, rmiId, callback, this, serialization)
|
onGenericObjectResponse(endPoint, connection, true, rmiId, callback, serialization)
|
||||||
}
|
}
|
||||||
is MethodRequest -> {
|
is MethodRequest -> {
|
||||||
/**
|
/**
|
||||||
|
@ -320,8 +325,8 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
val implObject = getImplObject<Any>(isGlobal, rmiObjectId, connection)
|
val implObject = getImplObject<Any>(isGlobal, rmiObjectId, connection)
|
||||||
|
|
||||||
if (implObject == null) {
|
if (implObject == null) {
|
||||||
logger.error("Unable to resolve implementation object for [global=$isGlobal, objectID=$rmiObjectId, connection=$connection")
|
endPoint.listenerManager.notifyError(connection,
|
||||||
|
NullPointerException("Unable to resolve implementation object for [global=$isGlobal, objectID=$rmiObjectId, connection=$connection"))
|
||||||
if (sendResponse) {
|
if (sendResponse) {
|
||||||
returnRmiMessage(connection,
|
returnRmiMessage(connection,
|
||||||
message,
|
message,
|
||||||
|
@ -365,7 +370,7 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
var insideResult: Any?
|
var insideResult: Any?
|
||||||
try {
|
try {
|
||||||
// args!! is safe to do here (even though it doesn't make sense)
|
// args!! is safe to do here (even though it doesn't make sense)
|
||||||
insideResult = cachedMethod.invoke(connection, implObject, args)
|
// insideResult = cachedMethod.invoke(connection, implObject, args)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
insideResult = ex.cause
|
insideResult = ex.cause
|
||||||
// added to prevent a stack overflow when references is false, (because 'cause' == "this").
|
// added to prevent a stack overflow when references is false, (because 'cause' == "this").
|
||||||
|
@ -377,12 +382,9 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
else {
|
else {
|
||||||
insideResult.initCause(null)
|
insideResult.initCause(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
RmiUtils.cleanStackTraceForImpl(insideResult as Exception, true)
|
|
||||||
val fancyName = RmiUtils.makeFancyMethodName(cachedMethod)
|
|
||||||
logger.error("Error invoking method: $fancyName", insideResult)
|
|
||||||
}
|
}
|
||||||
insideResult
|
// insideResult
|
||||||
|
Exception(":ASDASDUJHAKDSGJFHAKHDLA")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,6 +397,15 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
// return value!
|
// return value!
|
||||||
suspendResult = null
|
suspendResult = null
|
||||||
}
|
}
|
||||||
|
else if (suspendResult is Exception) {
|
||||||
|
RmiUtils.cleanStackTraceForImpl(suspendResult, true)
|
||||||
|
|
||||||
|
val fancyName = RmiUtils.makeFancyMethodName(cachedMethod)
|
||||||
|
val exception = Exception("Error invoking method: $fancyName", suspendResult)
|
||||||
|
RmiUtils.cleanStackTraceForImpl(exception, true)
|
||||||
|
|
||||||
|
endPoint.listenerManager.notifyError(connection, exception)
|
||||||
|
}
|
||||||
|
|
||||||
if (sendResponse) {
|
if (sendResponse) {
|
||||||
returnRmiMessage(connection, message, suspendResult, logger)
|
returnRmiMessage(connection, message, suspendResult, logger)
|
||||||
|
@ -448,7 +459,7 @@ internal class RmiManagerGlobal(logger: KLogger,
|
||||||
/**
|
/**
|
||||||
* called on "server"
|
* called on "server"
|
||||||
*/
|
*/
|
||||||
private suspend fun onGlobalObjectCreateRequest(endPoint: EndPoint<*>, connection: Connection, message: GlobalObjectCreateRequest, logger: KLogger) {
|
private suspend fun onGlobalObjectCreateRequest(endPoint: EndPoint<CONNECTION>, connection: Connection, message: GlobalObjectCreateRequest, logger: KLogger) {
|
||||||
val interfaceClassId = RmiUtils.unpackLeft(message.packedIds)
|
val interfaceClassId = RmiUtils.unpackLeft(message.packedIds)
|
||||||
val callbackId = RmiUtils.unpackRight(message.packedIds)
|
val callbackId = RmiUtils.unpackRight(message.packedIds)
|
||||||
val objectParameters = message.objectParameters
|
val objectParameters = message.objectParameters
|
||||||
|
|
|
@ -477,10 +477,13 @@ object RmiUtils {
|
||||||
*
|
*
|
||||||
* We do this because these stack frames are not useful in resolving exception handling from a users perspective, and only clutter the stacktrace.
|
* We do this because these stack frames are not useful in resolving exception handling from a users perspective, and only clutter the stacktrace.
|
||||||
*/
|
*/
|
||||||
fun cleanStackTraceForProxy(localThrowable: Throwable, invokingClass: Class<*>, remoteException: Exception? = null) {
|
fun cleanStackTraceForProxy(localException: Exception, remoteException: Exception? = null) {
|
||||||
val myClassName = invokingClass.name
|
val myClassName = RmiClient::class.java.name
|
||||||
val stackTrace = localThrowable.stackTrace
|
val stackTrace = localException.stackTrace
|
||||||
var newStartIndex = 0
|
var newStartIndex = 0
|
||||||
|
var newEndIndex = stackTrace.size-1
|
||||||
|
|
||||||
|
// step 1: Find the start of our method invocation
|
||||||
for (element in stackTrace) {
|
for (element in stackTrace) {
|
||||||
newStartIndex++
|
newStartIndex++
|
||||||
|
|
||||||
|
@ -491,14 +494,24 @@ object RmiUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// step 2: now we have to find the END index, since a proxy invocation ALWAYS happens starting from our network stack
|
||||||
|
for (i in stackTrace.size-1 downTo 0) {
|
||||||
|
newEndIndex--
|
||||||
|
|
||||||
|
val stackClassName = stackTrace[i].className
|
||||||
|
if (stackClassName.startsWith("dorkbox.network.rmi.")) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (remoteException == null) {
|
if (remoteException == null) {
|
||||||
// no remote exception, just cleanup our own callstack
|
// no remote exception, just cleanup our own callstack
|
||||||
localThrowable.stackTrace = stackTrace.copyOfRange(newStartIndex, stackTrace.size)
|
localException.stackTrace = stackTrace.copyOfRange(newStartIndex, newEndIndex)
|
||||||
} else {
|
} else {
|
||||||
// merge this info into the remote exception, so we can get the correct call stack info
|
// merge this info into the remote exception, so we can get the correct call stack info
|
||||||
val newStack = Array<StackTraceElement>(remoteException.stackTrace.size + stackTrace.size - newStartIndex) { stackTrace[0] }
|
val newStack = Array<StackTraceElement>(remoteException.stackTrace.size + newEndIndex - newStartIndex) { stackTrace[0] }
|
||||||
remoteException.stackTrace.copyInto(newStack)
|
remoteException.stackTrace.copyInto(newStack)
|
||||||
stackTrace.copyInto(newStack, remoteException.stackTrace.size, newStartIndex)
|
stackTrace.copyInto(newStack, remoteException.stackTrace.size, newStartIndex, newEndIndex)
|
||||||
|
|
||||||
remoteException.stackTrace = newStack
|
remoteException.stackTrace = newStack
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user