Fixed issues with reverse RMI and having the client register impl classes (instead of the server)

This commit is contained in:
nathan 2020-09-03 01:30:30 +02:00
parent b5342cd2f8
commit b1e92be50b
3 changed files with 81 additions and 28 deletions

View File

@ -63,7 +63,7 @@ internal abstract class ClassRegistration(val clazz: Class<*>, val serializer: S
// otherwise, we are OK to continue to register this // otherwise, we are OK to continue to register this
register(kryo) register(kryo)
if (serializer != null && overriddenSerializer != serializer) { if (serializer != null && overriddenSerializer != null && overriddenSerializer != serializer) {
info = "$info (Replaced $overriddenSerializer)" info = "$info (Replaced $overriddenSerializer)"
} }

View File

@ -45,7 +45,7 @@ import dorkbox.network.rmi.messages.RmiServerSerializer
* If the impl object 'lives' on the SERVER, then the server must tell the client about the iface ID * If the impl object 'lives' on the SERVER, then the server must tell the client about the iface ID
*/ */
internal class ClassRegistrationForRmi(ifaceClass: Class<*>, internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
val implClass: Class<*>?, var implClass: Class<*>?,
serializer: RmiServerSerializer) : ClassRegistration(ifaceClass, serializer) { serializer: RmiServerSerializer) : ClassRegistration(ifaceClass, serializer) {
/** /**
* In general: * In general:
@ -106,21 +106,55 @@ internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
override fun register(kryo: KryoExtra, rmi: RmiHolder) { override fun register(kryo: KryoExtra, rmi: RmiHolder) {
// we override this, because we ALWAYS will call our RMI registration! // we override this, because we ALWAYS will call our RMI registration!
// have to get the ID for the interface (if it exists) if (implClass == null) {
val registration = kryo.classResolver.getRegistration(clazz) // this is ifaceClass, and must match what is defined on the rmi client // this means we are the RMI-CLIENT
if (registration != null) {
id = registration.id
// override that registration // see if we have the IMPL registered? NOT LIKELY, but possible
kryo.register(implClass, serializer, id) val existingId = rmi.ifaceToId[clazz]
} else { if (existingId != null) {
// now register the impl class implClass = rmi.idToImpl[existingId]
id = kryo.register(implClass, serializer).id }
if (implClass == null) {
// then we just register the interface class!
val registration = kryo.classResolver.getRegistration(clazz)
if (registration != null) {
id = registration.id
// override that registration
kryo.register(clazz, serializer, id)
}
else {
// just register the iface class
id = kryo.register(clazz, serializer).id
}
}
} }
if (implClass != null) {
// this means we are the RMI-SERVER
val registration = kryo.classResolver.getRegistration(implClass) // we cannot register iface classes!
if (registration != null) {
id = registration.id
// override that registration
kryo.register(implClass, serializer, id)
}
else {
// now register the impl class
id = kryo.register(implClass, serializer).id
}
}
// have to get the ID for the interface (if it exists)
info = if (implClass == null) { info = if (implClass == null) {
"Registered $id -> (RMI-CLIENT) ${clazz.name}" "Registered $id -> (RMI-CLIENT) ${clazz.name}"
} else { } else {
"Registered $id -> (RMI-SERVER) ${clazz.name} -> ${implClass.name}" "Registered $id -> (RMI-SERVER) ${clazz.name} -> ${implClass!!.name}"
} }
// now, we want to save the relationship between classes and kryoId // now, we want to save the relationship between classes and kryoId
@ -128,8 +162,10 @@ internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
rmi.idToIface[id] = clazz rmi.idToIface[id] = clazz
// we have to know what the IMPL class is so we can create it for a "createObject" RMI command // we have to know what the IMPL class is so we can create it for a "createObject" RMI command
rmi.implToId[implClass] = id if (implClass != null) {
rmi.idToImpl[id] = implClass rmi.implToId[implClass] = id
rmi.idToImpl[id] = implClass
}
} }
override fun getInfoArray(): Array<Any> { override fun getInfoArray(): Array<Any> {
@ -137,7 +173,7 @@ internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
return if (implClass == null) { return if (implClass == null) {
arrayOf(4, id, clazz.name, serializer!!::class.java.name, "") arrayOf(4, id, clazz.name, serializer!!::class.java.name, "")
} else { } else {
arrayOf(4, id, clazz.name, serializer!!::class.java.name, implClass.name) arrayOf(4, id, clazz.name, serializer!!::class.java.name, implClass!!.name)
} }
} }
} }

View File

@ -228,14 +228,22 @@ open class Serialization(private val references: Boolean = true, private val fac
* *
* This is NOT bi-directional, and this endpoint cannot access or create remote objects on the "remote client". * This is NOT bi-directional, and this endpoint cannot access or create remote objects on the "remote client".
* *
* @param ifaceClass this must be the interface class used for RMI
* @param implClass this must be the implementation class used for RMI
* If *null* it means that this endpoint is the rmi-client
* If *not-null* it means that this endpoint is the rmi-server
*
* @throws IllegalArgumentException if the iface/impl have previously been overridden * @throws IllegalArgumentException if the iface/impl have previously been overridden
*/ */
@Synchronized @Synchronized
fun <Iface, Impl : Iface> registerRmi(ifaceClass: Class<Iface>, implClass: Class<Impl>): Serialization { fun <Iface, Impl : Iface> registerRmi(ifaceClass: Class<Iface>, implClass: Class<Impl>? = null): Serialization {
require(!initialized.value) { "Serialization 'registerRmi(Class, Class)' cannot happen after client/server initialization!" } require(!initialized.value) { "Serialization 'registerRmi(Class, Class)' cannot happen after client/server initialization!" }
require(ifaceClass.isInterface) { "Cannot register an implementation for RMI access. It must be an interface." } require(ifaceClass.isInterface) { "Cannot register an implementation for RMI access. It must be an interface." }
require(!implClass.isInterface) { "Cannot register an interface for RMI implementations. It must be an implementation." }
if (implClass != null) {
require(!implClass.isInterface) { "Cannot register an interface for RMI implementations. It must be an implementation." }
}
classesToRegister.add(ClassRegistrationForRmi(ifaceClass, implClass, rmiServerSerializer)) classesToRegister.add(ClassRegistrationForRmi(ifaceClass, implClass, rmiServerSerializer))
return this return this
@ -332,7 +340,6 @@ open class Serialization(private val references: Boolean = true, private val fac
return true return true
} }
require(classesToRegister.isEmpty()) { "Unable to initialize a non-empty class registration state! Make sure there are no serialization registrations for the client!" }
initializeClient(kryoRegistrationDetailsFromServer) initializeClient(kryoRegistrationDetailsFromServer)
} }
} }
@ -459,7 +466,18 @@ open class Serialization(private val references: Boolean = true, private val fac
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun initializeClient(kryoRegistrationDetailsFromServer: ByteArray): Boolean { private fun initializeClient(kryoRegistrationDetailsFromServer: ByteArray): Boolean {
val kryo = initKryo() // we have to allow CUSTOM classes to register (where the order does not matter), so that if the CLIENT is the RMI-SERVER, it can
// specify IMPL classes for RMI.
classesToRegister.forEach { registration ->
require(registration is ClassRegistrationForRmi) { "Unable to initialize a class registrations for anything OTHER than RMI!! To fix this, remove ${registration.clazz}" }
}
val classesToRegisterForRmi = listOf(*classesToRegister.toTypedArray()) as List<ClassRegistrationForRmi>
classesToRegister.clear()
var kryo = initKryo()
val input = AeronInput(kryoRegistrationDetailsFromServer) val input = AeronInput(kryoRegistrationDetailsFromServer)
val clientClassRegistrations = kryo.readCompressed(logger, input, kryoRegistrationDetailsFromServer.size) as Array<Array<Any>> val clientClassRegistrations = kryo.readCompressed(logger, input, kryoRegistrationDetailsFromServer.size) as Array<Array<Any>>
@ -486,16 +504,18 @@ open class Serialization(private val references: Boolean = true, private val fac
// we literally want everything to be 100% the same. // we literally want everything to be 100% the same.
// the only WEIRD case is when the client == rmi-server (in which case, the IMPL object is on the client) // the only WEIRD case is when the client == rmi-server (in which case, the IMPL object is on the client)
// for this, the server (rmi-client) WILL ALSO have the same registration info. (bi-directional RMI, but not really) // for this, the server (rmi-client) WILL ALSO have the same registration info. (bi-directional RMI, but not really)
val implClazzName = bytes[4] as String val implClassName = bytes[4] as String
var implClass: Class<*>? = null var implClass: Class<*>? = classesToRegisterForRmi.firstOrNull { it.clazz.name == clazzName }?.implClass
if (implClazzName.isNotEmpty()) { // if we do not have the impl class specified by the registrations for RMI, then do a lookup to see if we have access to it as the client
if (implClass == null) {
try { try {
implClass = Class.forName(implClazzName) implClass = Class.forName(implClassName)
} catch (ignored: Exception) { } catch (ignored: Exception) {
} }
} }
// implClass MIGHT BE NULL!
classesToRegister.add(ClassRegistrationForRmi(clazz, implClass, rmiServerSerializer)) classesToRegister.add(ClassRegistrationForRmi(clazz, implClass, rmiServerSerializer))
} }
@ -512,11 +532,8 @@ open class Serialization(private val references: Boolean = true, private val fac
// we have to re-init so the registrations are set! // we have to re-init so the registrations are set!
initKryo() initKryo()
// now do a round-trip through the server serialization to make sure our byte arrays are THE SAME. // now do a round-trip through the class registrations
initializeClassRegistrations() return initializeClassRegistrations()
// verify the registration ID data is THE SAME!
return savedRegistrationDetails.contentEquals(kryoRegistrationDetailsFromServer)
} }
/** /**