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
register(kryo)
if (serializer != null && overriddenSerializer != serializer) {
if (serializer != null && overriddenSerializer != null && overriddenSerializer != serializer) {
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
*/
internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
val implClass: Class<*>?,
var implClass: Class<*>?,
serializer: RmiServerSerializer) : ClassRegistration(ifaceClass, serializer) {
/**
* In general:
@ -106,21 +106,55 @@ internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
override fun register(kryo: KryoExtra, rmi: RmiHolder) {
// we override this, because we ALWAYS will call our RMI registration!
// have to get the ID for the interface (if it exists)
val registration = kryo.classResolver.getRegistration(clazz) // this is ifaceClass, and must match what is defined on the rmi client
if (registration != null) {
id = registration.id
if (implClass == null) {
// this means we are the RMI-CLIENT
// override that registration
kryo.register(implClass, serializer, id)
} else {
// now register the impl class
id = kryo.register(implClass, serializer).id
// see if we have the IMPL registered? NOT LIKELY, but possible
val existingId = rmi.ifaceToId[clazz]
if (existingId != null) {
implClass = rmi.idToImpl[existingId]
}
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) {
"Registered $id -> (RMI-CLIENT) ${clazz.name}"
} 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
@ -128,8 +162,10 @@ internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
rmi.idToIface[id] = clazz
// we have to know what the IMPL class is so we can create it for a "createObject" RMI command
rmi.implToId[implClass] = id
rmi.idToImpl[id] = implClass
if (implClass != null) {
rmi.implToId[implClass] = id
rmi.idToImpl[id] = implClass
}
}
override fun getInfoArray(): Array<Any> {
@ -137,7 +173,7 @@ internal class ClassRegistrationForRmi(ifaceClass: Class<*>,
return if (implClass == null) {
arrayOf(4, id, clazz.name, serializer!!::class.java.name, "")
} 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".
*
* @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
*/
@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(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))
return this
@ -332,7 +340,6 @@ open class Serialization(private val references: Boolean = true, private val fac
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)
}
}
@ -459,7 +466,18 @@ open class Serialization(private val references: Boolean = true, private val fac
@Suppress("UNCHECKED_CAST")
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 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.
// 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)
val implClazzName = bytes[4] as String
var implClass: Class<*>? = null
val implClassName = bytes[4] as String
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 {
implClass = Class.forName(implClazzName)
implClass = Class.forName(implClassName)
} catch (ignored: Exception) {
}
}
// implClass MIGHT BE NULL!
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!
initKryo()
// now do a round-trip through the server serialization to make sure our byte arrays are THE SAME.
initializeClassRegistrations()
// verify the registration ID data is THE SAME!
return savedRegistrationDetails.contentEquals(kryoRegistrationDetailsFromServer)
// now do a round-trip through the class registrations
return initializeClassRegistrations()
}
/**