RMI proxy objects no longer save "globally", but instead now are saved per connection

This commit is contained in:
nathan 2020-08-20 23:28:12 +02:00
parent 779861b072
commit c117c90ee5
6 changed files with 83 additions and 75 deletions

View File

@ -30,7 +30,7 @@ import dorkbox.network.connection.UdpMediaDriverConnection
import dorkbox.network.handshake.ClientHandshake import dorkbox.network.handshake.ClientHandshake
import dorkbox.network.rmi.RemoteObject import dorkbox.network.rmi.RemoteObject
import dorkbox.network.rmi.RemoteObjectStorage import dorkbox.network.rmi.RemoteObjectStorage
import dorkbox.network.rmi.RmiManagerForConnections import dorkbox.network.rmi.RmiManagerConnections
import dorkbox.network.rmi.TimeoutException import dorkbox.network.rmi.TimeoutException
import dorkbox.util.exceptions.SecurityException import dorkbox.util.exceptions.SecurityException
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -72,7 +72,7 @@ open class Client<CONNECTION : Connection>(config: Configuration = Configuration
private val previousClosedConnectionActivity: Long = 0 private val previousClosedConnectionActivity: Long = 0
private val handshake = ClientHandshake(logger, config, crypto, listenerManager) private val handshake = ClientHandshake(logger, config, crypto, listenerManager)
private val rmiConnectionSupport = RmiManagerForConnections(logger, rmiGlobalSupport, serialization, actionDispatch) private val rmiConnectionSupport = RmiManagerConnections(logger, rmiGlobalSupport, serialization, actionDispatch)
init { init {
// have to do some basic validation of our configuration // have to do some basic validation of our configuration
@ -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(): RmiManagerForConnections { override fun getRmiConnectionSupport(): RmiManagerConnections {
return rmiConnectionSupport return rmiConnectionSupport
} }

View File

@ -22,8 +22,8 @@ import dorkbox.network.ServerConfiguration
import dorkbox.network.aeron.CoroutineIdleStrategy import dorkbox.network.aeron.CoroutineIdleStrategy
import dorkbox.network.connection.ping.PingMessage import dorkbox.network.connection.ping.PingMessage
import dorkbox.network.ipFilter.IpFilterRule import dorkbox.network.ipFilter.IpFilterRule
import dorkbox.network.rmi.RmiManagerForConnections import dorkbox.network.rmi.RmiManagerConnections
import dorkbox.network.rmi.RmiMessageManager import dorkbox.network.rmi.RmiManagerGlobal
import dorkbox.network.rmi.messages.RmiMessage import dorkbox.network.rmi.messages.RmiMessage
import dorkbox.network.serialization.KryoExtra import dorkbox.network.serialization.KryoExtra
import dorkbox.network.serialization.NetworkSerializationManager import dorkbox.network.serialization.NetworkSerializationManager
@ -133,7 +133,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 = RmiMessageManager(logger, actionDispatch, config.serialization) internal val rmiGlobalSupport = RmiManagerGlobal(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,8 +318,8 @@ 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() : RmiManagerForConnections { internal open fun getRmiConnectionSupport() : RmiManagerConnections {
return RmiManagerForConnections(logger, rmiGlobalSupport, serialization, actionDispatch) return RmiManagerConnections(logger, rmiGlobalSupport, serialization, actionDispatch)
} }
/** /**

View File

@ -124,7 +124,7 @@ internal class RmiClient(val isGlobal: Boolean,
// manage all of the RemoteObject proxy methods // manage all of the RemoteObject proxy methods
when (method) { when (method) {
closeMethod -> { closeMethod -> {
rmiObjectCache.removeProxyObject(rmiObjectId) connection.rmiConnectionSupport.removeProxyObject(rmiObjectId)
return null return null
} }

View File

@ -20,13 +20,31 @@ import dorkbox.network.connection.EndPoint
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 kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import mu.KLogger import mu.KLogger
internal class RmiManagerForConnections(logger: KLogger, internal class RmiManagerConnections(logger: KLogger,
val rmiGlobalSupport: RmiMessageManager, val rmiGlobalSupport: RmiManagerGlobal,
private val serialization: NetworkSerializationManager, private val serialization: NetworkSerializationManager,
actionDispatch: CoroutineScope) : RmiObjectCache(logger, actionDispatch) { actionDispatch: CoroutineScope) : RmiObjectCache(logger, actionDispatch) {
private val proxyObjects = LockFreeIntMap<RemoteObject>()
/**
* Removes a proxy object from the system
*/
fun removeProxyObject(rmiId: Int) {
proxyObjects.remove(rmiId)
}
fun getProxyObject(rmiId: Int): RemoteObject? {
return proxyObjects[rmiId]
}
fun saveProxyObject(rmiId: Int, remoteObject: RemoteObject) {
proxyObjects.put(rmiId, remoteObject)
}
private fun <Iface> createProxyObject(isGlobalObject: Boolean, private fun <Iface> createProxyObject(isGlobalObject: Boolean,
connection: Connection, connection: Connection,
@ -37,7 +55,7 @@ internal class RmiManagerForConnections(logger: KLogger,
// so we can just instantly create the proxy object (or get the cached one) // so we can just instantly create the proxy object (or get the cached one)
var proxyObject = getProxyObject(objectId) var proxyObject = getProxyObject(objectId)
if (proxyObject == null) { if (proxyObject == null) {
proxyObject = RmiMessageManager.createProxyObject(isGlobalObject, connection, serialization, rmiGlobalSupport, endPoint.type.simpleName, objectId, interfaceClass) proxyObject = RmiManagerGlobal.createProxyObject(isGlobalObject, connection, serialization, rmiGlobalSupport, endPoint.type.simpleName, objectId, interfaceClass)
saveProxyObject(objectId, proxyObject) saveProxyObject(objectId, proxyObject)
} }
@ -95,7 +113,7 @@ internal class RmiManagerForConnections(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
RmiMessageManager.scanImplForRmiFields(logger, implObject) { RmiManagerGlobal.scanImplForRmiFields(logger, implObject) {
saveImplObject(it) saveImplObject(it)
} }
} else { } else {
@ -110,4 +128,9 @@ internal class RmiManagerForConnections(logger: KLogger,
connection.send(response) connection.send(response)
} }
override fun close() {
proxyObjects.clear()
super.close()
}
} }

View File

@ -26,17 +26,17 @@ import dorkbox.network.rmi.messages.MethodRequest
import dorkbox.network.rmi.messages.MethodResponse import dorkbox.network.rmi.messages.MethodResponse
import dorkbox.network.serialization.NetworkSerializationManager import dorkbox.network.serialization.NetworkSerializationManager
import dorkbox.util.classes.ClassHelper import dorkbox.util.classes.ClassHelper
import dorkbox.util.collections.LockFreeIntMap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mu.KLogger import mu.KLogger
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
import java.util.* import java.util.*
internal class RmiMessageManager(logger: KLogger, internal class RmiManagerGlobal(logger: KLogger,
actionDispatch: CoroutineScope, actionDispatch: CoroutineScope,
internal val serialization: NetworkSerializationManager) : RmiObjectCache(logger, actionDispatch) { internal val serialization: NetworkSerializationManager) : RmiObjectCache(logger, actionDispatch) {
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
* remotely. * remotely.
@ -74,7 +74,7 @@ internal class RmiMessageManager(logger: KLogger,
// This is the interface inheritance by the proxy object // This is the interface inheritance by the proxy object
val interfaces: Array<Class<*>> = arrayOf(RemoteObject::class.java, interfaceClass) val interfaces: Array<Class<*>> = arrayOf(RemoteObject::class.java, interfaceClass)
return Proxy.newProxyInstance(RmiMessageManager::class.java.classLoader, interfaces, proxyObject) as RemoteObject return Proxy.newProxyInstance(RmiManagerGlobal::class.java.classLoader, interfaces, proxyObject) as RemoteObject
} }
/** /**
@ -129,42 +129,10 @@ internal class RmiMessageManager(logger: KLogger,
} }
} }
} }
/**
* called on "client"
*/
private fun onGenericObjectResponse(endPoint: EndPoint<*>, connection: Connection, logger: KLogger,
isGlobal: Boolean, rmiId: Int, callback: suspend (Int, Any) -> Unit,
rmiObjectCache: RmiObjectCache, serialization: NetworkSerializationManager) {
// we only create the proxy + execute the callback if the RMI id is valid!
if (rmiId == RemoteObjectStorage.INVALID_RMI) {
logger.error {
"RMI ID '${rmiId}' is invalid. Unable to create RMI object on server."
}
return
}
val interfaceClass = ClassHelper.getGenericParameterAsClassForSuperClass(RemoteObjectCallback::class.java, callback.javaClass, 1)
// create the client-side proxy object, if possible
var proxyObject = rmiObjectCache.getProxyObject(rmiId)
if (proxyObject == null) {
proxyObject = createProxyObject(isGlobal, connection, serialization, rmiObjectCache, endPoint.type.simpleName, rmiId, interfaceClass)
rmiObjectCache.saveProxyObject(rmiId, proxyObject)
}
// this should be executed on a NEW coroutine!
endPoint.actionDispatch.launch {
try {
callback(rmiId, proxyObject)
} catch (e: Exception) {
logger.error("Error getting or creating the remote object $interfaceClass", e)
}
}
}
} }
private val proxyObjects = LockFreeIntMap<RemoteObject>()
// this is used for all connection specific ones as well. // this is used for all connection specific ones as well.
private val remoteObjectCreationCallbacks = RemoteObjectStorage(logger) private val remoteObjectCreationCallbacks = RemoteObjectStorage(logger)
@ -243,17 +211,52 @@ internal class RmiMessageManager(logger: KLogger,
remoteObjectCreationCallbacks.close() remoteObjectCreationCallbacks.close()
} }
/**
* called on "client"
*/
private fun onGenericObjectResponse(endPoint: EndPoint<*>, connection: Connection, logger: KLogger,
isGlobal: Boolean, rmiId: Int, callback: suspend (Int, Any) -> Unit,
rmiObjectCache: RmiObjectCache, serialization: NetworkSerializationManager) {
// we only create the proxy + execute the callback if the RMI id is valid!
if (rmiId == RemoteObjectStorage.INVALID_RMI) {
logger.error {
"RMI ID '${rmiId}' is invalid. Unable to create RMI object on server."
}
return
}
val interfaceClass = ClassHelper.getGenericParameterAsClassForSuperClass(RemoteObjectCallback::class.java, callback.javaClass, 1)
// create the client-side proxy object, if possible. This MUST be an object that is saved for the connection
var proxyObject = connection.rmiConnectionSupport.getProxyObject(rmiId)
if (proxyObject == null) {
proxyObject = createProxyObject(isGlobal, connection, serialization, rmiObjectCache, endPoint.type.simpleName, rmiId, interfaceClass)
connection.rmiConnectionSupport.saveProxyObject(rmiId, proxyObject)
}
// this should be executed on a NEW coroutine!
endPoint.actionDispatch.launch {
try {
callback(rmiId, proxyObject)
} catch (e: Exception) {
logger.error("Error getting or creating the remote object $interfaceClass", e)
}
}
}
/** /**
* on the connection+client to get a global remote object (that exists on the server) * on the connection+client to get a global remote object (that exists on the server)
*/ */
fun <Iface> getGlobalRemoteObject(connection: Connection, endPoint: EndPoint<*>, objectId: Int, interfaceClass: Class<Iface>): Iface { fun <Iface> getGlobalRemoteObject(connection: Connection, endPoint: EndPoint<*>, objectId: Int, interfaceClass: Class<Iface>): Iface {
// this immediately returns BECAUSE the object must have already been created on the server (this is why we specify the rmiId)! // this immediately returns BECAUSE the object must have already been created on the server (this is why we specify the rmiId)!
// so we can just instantly create the proxy object (or get the cached one) // so we can just instantly create the proxy object (or get the cached one). This MUST be an object that is saved for the connection
var proxyObject = getProxyObject(objectId) var proxyObject = connection.rmiConnectionSupport.getProxyObject(objectId)
if (proxyObject == null) { if (proxyObject == null) {
proxyObject = createProxyObject(true, connection, serialization, this, endPoint.type.simpleName, objectId, interfaceClass) proxyObject = createProxyObject(true, connection, serialization, this, endPoint.type.simpleName, objectId, interfaceClass)
saveProxyObject(objectId, proxyObject) connection.rmiConnectionSupport.saveProxyObject(objectId, proxyObject)
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")

View File

@ -15,7 +15,6 @@
*/ */
package dorkbox.network.rmi package dorkbox.network.rmi
import dorkbox.util.collections.LockFreeIntMap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import mu.KLogger import mu.KLogger
@ -29,7 +28,6 @@ internal open class RmiObjectCache(logger: KLogger, actionDispatch: CoroutineSco
private val responseStorage = RmiResponseManager(logger, actionDispatch) private val responseStorage = RmiResponseManager(logger, actionDispatch)
private val implObjects = RemoteObjectStorage(logger) private val implObjects = RemoteObjectStorage(logger)
private val proxyObjects = LockFreeIntMap<RemoteObject>()
fun saveImplObject(rmiObject: Any): Int { fun saveImplObject(rmiObject: Any): Int {
return implObjects.register(rmiObject) return implObjects.register(rmiObject)
@ -48,21 +46,6 @@ internal open class RmiObjectCache(logger: KLogger, actionDispatch: CoroutineSco
return implObjects.remove(rmiId) as T? return implObjects.remove(rmiId) as T?
} }
/**
* Removes a proxy object from the system
*/
fun removeProxyObject(rmiId: Int) {
proxyObjects.remove(rmiId)
}
fun getProxyObject(rmiId: Int): RemoteObject? {
return proxyObjects[rmiId]
}
fun saveProxyObject(rmiId: Int, remoteObject: RemoteObject) {
proxyObjects.put(rmiId, remoteObject)
}
fun getResponseStorage(): RmiResponseManager { fun getResponseStorage(): RmiResponseManager {
return responseStorage return responseStorage
} }
@ -70,6 +53,5 @@ internal open class RmiObjectCache(logger: KLogger, actionDispatch: CoroutineSco
open fun close() { open fun close() {
responseStorage.close() responseStorage.close()
implObjects.close() implObjects.close()
proxyObjects.clear()
} }
} }