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.rmi.RemoteObject
import dorkbox.network.rmi.RemoteObjectStorage
import dorkbox.network.rmi.RmiManagerForConnections
import dorkbox.network.rmi.RmiManagerConnections
import dorkbox.network.rmi.TimeoutException
import dorkbox.util.exceptions.SecurityException
import kotlinx.coroutines.launch
@ -72,7 +72,7 @@ open class Client<CONNECTION : Connection>(config: Configuration = Configuration
private val previousClosedConnectionActivity: Long = 0
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 {
// 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
*/
override fun getRmiConnectionSupport(): RmiManagerForConnections {
override fun getRmiConnectionSupport(): RmiManagerConnections {
return rmiConnectionSupport
}

View File

@ -22,8 +22,8 @@ import dorkbox.network.ServerConfiguration
import dorkbox.network.aeron.CoroutineIdleStrategy
import dorkbox.network.connection.ping.PingMessage
import dorkbox.network.ipFilter.IpFilterRule
import dorkbox.network.rmi.RmiManagerForConnections
import dorkbox.network.rmi.RmiMessageManager
import dorkbox.network.rmi.RmiManagerConnections
import dorkbox.network.rmi.RmiManagerGlobal
import dorkbox.network.rmi.messages.RmiMessage
import dorkbox.network.serialization.KryoExtra
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
val settingsStore: SettingsStore
internal val rmiGlobalSupport = RmiMessageManager(logger, actionDispatch, config.serialization)
internal val rmiGlobalSupport = RmiManagerGlobal(logger, actionDispatch, config.serialization)
init {
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
* from a "global" context
*/
internal open fun getRmiConnectionSupport() : RmiManagerForConnections {
return RmiManagerForConnections(logger, rmiGlobalSupport, serialization, actionDispatch)
internal open fun getRmiConnectionSupport() : RmiManagerConnections {
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
when (method) {
closeMethod -> {
rmiObjectCache.removeProxyObject(rmiObjectId)
connection.rmiConnectionSupport.removeProxyObject(rmiObjectId)
return null
}

View File

@ -20,13 +20,31 @@ import dorkbox.network.connection.EndPoint
import dorkbox.network.rmi.messages.ConnectionObjectCreateRequest
import dorkbox.network.rmi.messages.ConnectionObjectCreateResponse
import dorkbox.network.serialization.NetworkSerializationManager
import dorkbox.util.collections.LockFreeIntMap
import kotlinx.coroutines.CoroutineScope
import mu.KLogger
internal class RmiManagerForConnections(logger: KLogger,
val rmiGlobalSupport: RmiMessageManager,
private val serialization: NetworkSerializationManager,
actionDispatch: CoroutineScope) : RmiObjectCache(logger, actionDispatch) {
internal class RmiManagerConnections(logger: KLogger,
val rmiGlobalSupport: RmiManagerGlobal,
private val serialization: NetworkSerializationManager,
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,
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)
var proxyObject = getProxyObject(objectId)
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)
}
@ -95,7 +113,7 @@ internal class RmiManagerForConnections(logger: KLogger,
// this means we could register this object.
// next, scan this object to see if there are any RMI fields
RmiMessageManager.scanImplForRmiFields(logger, implObject) {
RmiManagerGlobal.scanImplForRmiFields(logger, implObject) {
saveImplObject(it)
}
} else {
@ -110,4 +128,9 @@ internal class RmiManagerForConnections(logger: KLogger,
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.serialization.NetworkSerializationManager
import dorkbox.util.classes.ClassHelper
import dorkbox.util.collections.LockFreeIntMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mu.KLogger
import java.lang.reflect.Proxy
import java.util.*
internal class RmiMessageManager(logger: KLogger,
actionDispatch: CoroutineScope,
internal val serialization: NetworkSerializationManager) : RmiObjectCache(logger, actionDispatch) {
internal class RmiManagerGlobal(logger: KLogger,
actionDispatch: CoroutineScope,
internal val serialization: NetworkSerializationManager) : RmiObjectCache(logger, actionDispatch) {
companion object {
/**
* Returns a proxy object that implements the specified interface, and the methods invoked on the proxy object will be invoked
* remotely.
@ -74,7 +74,7 @@ internal class RmiMessageManager(logger: KLogger,
// This is the interface inheritance by the proxy object
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.
private val remoteObjectCreationCallbacks = RemoteObjectStorage(logger)
@ -243,17 +211,52 @@ internal class RmiMessageManager(logger: KLogger,
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)
*/
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)!
// so we can just instantly create the proxy object (or get the cached one)
var proxyObject = getProxyObject(objectId)
// 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 = connection.rmiConnectionSupport.getProxyObject(objectId)
if (proxyObject == null) {
proxyObject = createProxyObject(true, connection, serialization, this, endPoint.type.simpleName, objectId, interfaceClass)
saveProxyObject(objectId, proxyObject)
connection.rmiConnectionSupport.saveProxyObject(objectId, proxyObject)
}
@Suppress("UNCHECKED_CAST")

View File

@ -15,7 +15,6 @@
*/
package dorkbox.network.rmi
import dorkbox.util.collections.LockFreeIntMap
import kotlinx.coroutines.CoroutineScope
import mu.KLogger
@ -29,7 +28,6 @@ internal open class RmiObjectCache(logger: KLogger, actionDispatch: CoroutineSco
private val responseStorage = RmiResponseManager(logger, actionDispatch)
private val implObjects = RemoteObjectStorage(logger)
private val proxyObjects = LockFreeIntMap<RemoteObject>()
fun saveImplObject(rmiObject: Any): Int {
return implObjects.register(rmiObject)
@ -48,21 +46,6 @@ internal open class RmiObjectCache(logger: KLogger, actionDispatch: CoroutineSco
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 {
return responseStorage
}
@ -70,6 +53,5 @@ internal open class RmiObjectCache(logger: KLogger, actionDispatch: CoroutineSco
open fun close() {
responseStorage.close()
implObjects.close()
proxyObjects.clear()
}
}