Fixed bug with registration detection for RMI types, fixed bug sending wrong class name for registration validation.
This commit is contained in:
parent
618168b68b
commit
6bd93ac8ea
|
@ -34,9 +34,9 @@ internal open class ClassRegistration(var clazz: Class<*>) {
|
||||||
|
|
||||||
fun getInfoArray(): Array<Any> {
|
fun getInfoArray(): Array<Any> {
|
||||||
return if (serializer != null) {
|
return if (serializer != null) {
|
||||||
arrayOf(id, clazz::class.java.name, serializer!!::class.java.name)
|
arrayOf(id, clazz.name, serializer!!::class.java.name)
|
||||||
} else {
|
} else {
|
||||||
arrayOf(id, clazz::class.java.name, "")
|
arrayOf(id, clazz.name, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,12 +125,9 @@ interface NetworkSerializationManager : SerializationManager<DirectBuffer> {
|
||||||
/**
|
/**
|
||||||
* There is additional overhead to using RMI.
|
* There is additional overhead to using RMI.
|
||||||
*
|
*
|
||||||
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
* - This is for the side where the object lives
|
||||||
* return value which is not [ignored][dorkbox.network.rmi.RemoteObject.setAsync], an extra byte is written.
|
|
||||||
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
|
||||||
*
|
*
|
||||||
*
|
* This enables a us, the "server" to send objects to a "remote endpoint"
|
||||||
* Enable a "remote endpoint" to access methods and create objects (RMI) for this endpoint.
|
|
||||||
*
|
*
|
||||||
* 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".
|
||||||
*
|
*
|
||||||
|
@ -138,6 +135,7 @@ interface NetworkSerializationManager : SerializationManager<DirectBuffer> {
|
||||||
*/
|
*/
|
||||||
fun <Iface, Impl : Iface> registerRmi(ifaceClass: Class<Iface>, implClass: Class<Impl>): NetworkSerializationManager
|
fun <Iface, Impl : Iface> registerRmi(ifaceClass: Class<Iface>, implClass: Class<Impl>): NetworkSerializationManager
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the cached methods for the specified class ID
|
* Gets the cached methods for the specified class ID
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -35,7 +35,6 @@ import dorkbox.objectPool.ObjectPool
|
||||||
import dorkbox.objectPool.PoolableObject
|
import dorkbox.objectPool.PoolableObject
|
||||||
import dorkbox.os.OS
|
import dorkbox.os.OS
|
||||||
import dorkbox.util.serialization.SerializationDefaults
|
import dorkbox.util.serialization.SerializationDefaults
|
||||||
import io.netty.buffer.Unpooled
|
|
||||||
import org.agrona.DirectBuffer
|
import org.agrona.DirectBuffer
|
||||||
import org.agrona.MutableDirectBuffer
|
import org.agrona.MutableDirectBuffer
|
||||||
import org.agrona.collections.Int2ObjectHashMap
|
import org.agrona.collections.Int2ObjectHashMap
|
||||||
|
@ -72,8 +71,6 @@ class Serialization(references: Boolean,
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CLASS_REGISTRATION_VALIDATION_FRAGMENT_SIZE = 400
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additionally, this serialization manager will register the entire class+interface hierarchy for an object. If you want to specify a
|
* Additionally, this serialization manager will register the entire class+interface hierarchy for an object. If you want to specify a
|
||||||
* serialization scheme for a specific class in an objects hierarchy, you must register that first.
|
* serialization scheme for a specific class in an objects hierarchy, you must register that first.
|
||||||
|
@ -364,17 +361,6 @@ class Serialization(references: Boolean,
|
||||||
mergedRegistrations.sortBy { it.id }
|
mergedRegistrations.sortBy { it.id }
|
||||||
|
|
||||||
|
|
||||||
// TODO? is this next part necessary?
|
|
||||||
// next, go through all of the registrations and see WHICH ones are actually for RMI (and need the remote-object-serializer) and
|
|
||||||
// which ones do not need RMI stuff.
|
|
||||||
// We know this 2 ways:
|
|
||||||
// 1) the class will be registered via "ClassRegistrationIfaceAndImpl"
|
|
||||||
// 2) the class will be an interface with NO DEFINED serializer
|
|
||||||
// val interfaceOnlyRegistrations = mergedRegistrations.filter { it.clazz.isInterface && it.serializer == null }
|
|
||||||
// mergedRegistrations.forEach { classRegistration ->
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
// now all of the registrations are IN ORDER and MERGED (save back to original array)
|
// now all of the registrations are IN ORDER and MERGED (save back to original array)
|
||||||
|
|
||||||
|
|
||||||
|
@ -383,8 +369,6 @@ class Serialization(references: Boolean,
|
||||||
classesToRegister.addAll(mergedRegistrations)
|
classesToRegister.addAll(mergedRegistrations)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// now create the registration details, used to validate that the client/server have the EXACT same class registration setup
|
// now create the registration details, used to validate that the client/server have the EXACT same class registration setup
|
||||||
val registrationDetails = arrayListOf<Array<Any>>()
|
val registrationDetails = arrayListOf<Array<Any>>()
|
||||||
classesToRegister.forEach { classRegistration ->
|
classesToRegister.forEach { classRegistration ->
|
||||||
|
@ -416,18 +400,18 @@ class Serialization(references: Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
// save this as a byte array (so class registration validation during connection handshake is faster)
|
// save this as a byte array (so class registration validation during connection handshake is faster)
|
||||||
val buffer = Unpooled.buffer(CLASS_REGISTRATION_VALIDATION_FRAGMENT_SIZE)
|
val output = AeronOutput()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
kryo.writeCompressed(logger, buffer, registrationDetails.toTypedArray())
|
kryo.writeCompressed(logger, output, registrationDetails.toTypedArray())
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Unable to write compressed data for registration details", e)
|
logger.error("Unable to write compressed data for registration details", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
val length = buffer.readableBytes()
|
val length = output.position()
|
||||||
savedRegistrationDetails = ByteArray(length)
|
savedRegistrationDetails = ByteArray(length)
|
||||||
buffer.getBytes(0, savedRegistrationDetails, 0, length)
|
output.toBytes().copyInto(savedRegistrationDetails, 0, 0, length)
|
||||||
buffer.release()
|
output.close()
|
||||||
} finally {
|
} finally {
|
||||||
kryoPool.put(kryo)
|
kryoPool.put(kryo)
|
||||||
}
|
}
|
||||||
|
@ -459,52 +443,89 @@ class Serialization(references: Boolean,
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RMI details might be one reason the arrays are different
|
||||||
|
|
||||||
// now we need to figure out WHAT was screwed up so we know what to fix
|
// now we need to figure out WHAT was screwed up so we know what to fix
|
||||||
// NOTE: it could just be that the byte arrays are different, because java has a non-deterministic iteration of hash maps.
|
// NOTE: it could just be that the byte arrays are different, because java has a non-deterministic iteration of hash maps.
|
||||||
val kryo = takeKryo()
|
val kryo = takeKryo()
|
||||||
val byteBuf = Unpooled.wrappedBuffer(clientBytes)
|
val input = AeronInput(clientBytes)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var success = true
|
var success = true
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val clientClassRegistrations = kryo.readCompressed(logger, byteBuf, clientBytes.size) as Array<Array<Any>>
|
val clientClassRegistrations = kryo.readCompressed(logger, input, clientBytes.size) as Array<Array<Any>>
|
||||||
val lengthServer = classesToRegister.size
|
val lengthServer = classesToRegister.size
|
||||||
val lengthClient = clientClassRegistrations.size
|
val lengthClient = clientClassRegistrations.size
|
||||||
var index = 0
|
var index = 0
|
||||||
|
|
||||||
// list all of the registrations that are mis-matched between the server/client
|
// list all of the registrations that are mis-matched between the server/client
|
||||||
while (index < lengthServer) {
|
for (i in 0 until lengthServer) {
|
||||||
|
index = i
|
||||||
val classServer = classesToRegister[index]
|
val classServer = classesToRegister[index]
|
||||||
if (index < lengthClient) {
|
|
||||||
val classClient = clientClassRegistrations[index]
|
|
||||||
val idClient = classClient[0] as Int
|
|
||||||
val nameClient = classClient[1] as String
|
|
||||||
val serializerClient = classClient[2] as String
|
|
||||||
val idServer = classServer.id
|
|
||||||
val nameServer = classServer.clazz.name
|
|
||||||
val serializerServer = classServer.serializer?.javaClass?.name ?: ""
|
|
||||||
|
|
||||||
if (idClient != idServer || nameServer != nameClient || !serializerClient.equals(serializerServer, ignoreCase = true)) {
|
if (index >= lengthClient) {
|
||||||
|
success = false
|
||||||
|
logger.error("Missing client registration for {} -> {}", classServer.id, classServer.clazz.name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val classClient = clientClassRegistrations[index]
|
||||||
|
|
||||||
|
val idClient = classClient[0] as Int
|
||||||
|
val nameClient = classClient[1] as String
|
||||||
|
val serializerClient = classClient[2] as String
|
||||||
|
|
||||||
|
val idServer = classServer.id
|
||||||
|
val nameServer = classServer.clazz.name
|
||||||
|
val serializerServer = classServer.serializer?.javaClass?.name ?: ""
|
||||||
|
|
||||||
|
// JUST MAYBE this is a serializer for RMI. The client doesn't have to register for RMI stuff
|
||||||
|
// this logic is unwrapped, and seemingly complex in order to specifically check for this in a performant way
|
||||||
|
val idMatches = idClient == idServer
|
||||||
|
if (!idMatches) {
|
||||||
|
success = false
|
||||||
|
logger.error("Registration {} Client -> {} ({})", idClient, nameClient, serializerClient)
|
||||||
|
logger.error("Registration {} Server -> {} ({})", idServer, nameServer, serializerServer)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val nameMatches = nameServer == nameClient
|
||||||
|
if (!nameMatches) {
|
||||||
|
success = false
|
||||||
|
logger.error("Registration {} Client -> {} ({})", idClient, nameClient, serializerClient)
|
||||||
|
logger.error("Registration {} Server -> {} ({})", idServer, nameServer, serializerServer)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val serializerMatches = serializerServer == serializerClient
|
||||||
|
if (!serializerMatches) {
|
||||||
|
// JUST MAYBE this is a serializer for RMI. The client doesn't have to register for RMI stuff
|
||||||
|
|
||||||
|
if (serializerServer == objectResponseSerializer::class.java.name && serializerClient.isEmpty()) {
|
||||||
|
// this is for RMI!
|
||||||
|
} else {
|
||||||
success = false
|
success = false
|
||||||
logger.error("Registration {} Client -> {} ({})", idClient, nameClient, serializerClient)
|
logger.error("Registration {} Client -> {} ({})", idClient, nameClient, serializerClient)
|
||||||
logger.error("Registration {} Server -> {} ({})", idServer, nameServer, serializerServer)
|
logger.error("Registration {} Server -> {} ({})", idServer, nameServer, serializerServer)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
success = false
|
|
||||||
logger.error("Missing client registration for {} -> {}", classServer.id, classServer.clazz.name)
|
|
||||||
}
|
}
|
||||||
index++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +1 because we are going from index -> length
|
||||||
|
index++
|
||||||
|
|
||||||
// list all of the registrations that are missing on the server
|
// list all of the registrations that are missing on the server
|
||||||
if (index < lengthClient) {
|
if (index < lengthClient) {
|
||||||
success = false
|
success = false
|
||||||
while (index < lengthClient) {
|
for (i in index - 1 until lengthClient) {
|
||||||
val holderClass = clientClassRegistrations[index]
|
val holderClass = clientClassRegistrations[i]
|
||||||
val id = holderClass[0] as Int
|
val id = holderClass[0] as Int
|
||||||
val name = holderClass[1] as String
|
val name = holderClass[1] as String
|
||||||
val serializer = holderClass[2] as String
|
val serializer = holderClass[2] as String
|
||||||
logger.error("Missing server registration : {} -> {} ({})", id, name, serializer)
|
logger.error("Missing server registration : {} -> {} ({})", id, name, serializer)
|
||||||
index++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +535,7 @@ class Serialization(references: Boolean,
|
||||||
logger.error("Error [{}] during registration validation", e.message)
|
logger.error("Error [{}] during registration validation", e.message)
|
||||||
} finally {
|
} finally {
|
||||||
returnKryo(kryo)
|
returnKryo(kryo)
|
||||||
byteBuf.release()
|
input.close()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user