RMI is now more robust and forgiving
This commit is contained in:
parent
c927f87316
commit
33853d44e3
|
@ -53,7 +53,7 @@ class CachedMethod {
|
||||||
* in some cases, we want to override the cached method, with one that supports passing 'Connection' as the first argument. This is
|
* in some cases, we want to override the cached method, with one that supports passing 'Connection' as the first argument. This is
|
||||||
* completely OPTIONAL, however - greatly adds functionality to RMI methods.
|
* completely OPTIONAL, however - greatly adds functionality to RMI methods.
|
||||||
*/
|
*/
|
||||||
public boolean overriddenMethod;
|
public Method overriddenMethod;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public Serializer[] serializers;
|
public Serializer[] serializers;
|
||||||
|
@ -62,4 +62,10 @@ class CachedMethod {
|
||||||
Object invoke(final Connection connection, Object target, Object[] args) throws Exception {
|
Object invoke(final Connection connection, Object target, Object[] args) throws Exception {
|
||||||
return this.method.invoke(target, args);
|
return this.method.invoke(target, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
String toString() {
|
||||||
|
return "CachedMethod{" + method.getName() + ", methodClassID=" + methodClassID + ", methodIndex=" + methodIndex + '}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ public
|
||||||
class InvokeMethod implements RmiMessage {
|
class InvokeMethod implements RmiMessage {
|
||||||
public int objectID; // the registered kryo ID for the object
|
public int objectID; // the registered kryo ID for the object
|
||||||
|
|
||||||
|
// This class is NOT sent across the wire. We use a custom serializer to manage this.
|
||||||
public CachedMethod cachedMethod;
|
public CachedMethod cachedMethod;
|
||||||
public Object[] args;
|
public Object[] args;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.network.rmi;
|
package dorkbox.network.rmi;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.Kryo;
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
import com.esotericsoftware.kryo.KryoException;
|
import com.esotericsoftware.kryo.KryoException;
|
||||||
import com.esotericsoftware.kryo.Serializer;
|
import com.esotericsoftware.kryo.Serializer;
|
||||||
|
@ -47,6 +49,7 @@ import dorkbox.network.connection.KryoExtra;
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
public
|
public
|
||||||
InvokeMethodSerializer() {
|
InvokeMethodSerializer() {
|
||||||
|
@ -56,9 +59,12 @@ class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public
|
public
|
||||||
void write(final Kryo kryo, final Output output, final InvokeMethod object) {
|
void write(final Kryo kryo, final Output output, final InvokeMethod object) {
|
||||||
// System.err.println(":: objectID " + object.objectID);
|
if (DEBUG) {
|
||||||
// System.err.println(":: methodClassID " + object.cachedMethod.methodClassID);
|
System.err.println("WRITING");
|
||||||
// System.err.println(":: methodIndex " + object.cachedMethod.methodIndex);
|
System.err.println(":: objectID " + object.objectID);
|
||||||
|
System.err.println(":: methodClassID " + object.cachedMethod.methodClassID);
|
||||||
|
System.err.println(":: methodIndex " + object.cachedMethod.methodIndex);
|
||||||
|
}
|
||||||
|
|
||||||
output.writeInt(object.objectID, true);
|
output.writeInt(object.objectID, true);
|
||||||
output.writeInt(object.cachedMethod.methodClassID, true);
|
output.writeInt(object.cachedMethod.methodClassID, true);
|
||||||
|
@ -90,9 +96,12 @@ class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
||||||
int methodClassID = input.readInt(true);
|
int methodClassID = input.readInt(true);
|
||||||
byte methodIndex = input.readByte();
|
byte methodIndex = input.readByte();
|
||||||
|
|
||||||
// System.err.println(":: objectID " + objectID);
|
if (DEBUG) {
|
||||||
// System.err.println(":: methodClassID " + methodClassID);
|
System.err.println("READING");
|
||||||
// System.err.println(":: methodIndex " + methodIndex);
|
System.err.println(":: objectID " + objectID);
|
||||||
|
System.err.println(":: methodClassID " + methodClassID);
|
||||||
|
System.err.println(":: methodIndex " + methodIndex);
|
||||||
|
}
|
||||||
|
|
||||||
CachedMethod cachedMethod;
|
CachedMethod cachedMethod;
|
||||||
try {
|
try {
|
||||||
|
@ -107,10 +116,13 @@ class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
||||||
Object[] args;
|
Object[] args;
|
||||||
Serializer<?>[] serializers = cachedMethod.serializers;
|
Serializer<?>[] serializers = cachedMethod.serializers;
|
||||||
|
|
||||||
|
Method method;
|
||||||
int argStartIndex;
|
int argStartIndex;
|
||||||
|
|
||||||
if (cachedMethod.overriddenMethod) {
|
if (cachedMethod.overriddenMethod != null) {
|
||||||
// did we override our cached method? This is not common.
|
// did we override our cached method? This is not common.
|
||||||
|
method = cachedMethod.overriddenMethod;
|
||||||
|
|
||||||
// this is specifically when we override an interface method, with an implementation method + Connection parameter (@ index 0)
|
// this is specifically when we override an interface method, with an implementation method + Connection parameter (@ index 0)
|
||||||
argStartIndex = 1;
|
argStartIndex = 1;
|
||||||
|
|
||||||
|
@ -118,12 +130,15 @@ class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
||||||
args[0] = ((KryoExtra) kryo).connection;
|
args[0] = ((KryoExtra) kryo).connection;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
method = cachedMethod.method;
|
||||||
argStartIndex = 0;
|
argStartIndex = 0;
|
||||||
args = new Object[serializers.length];
|
args = new Object[serializers.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?>[] parameterTypes = cachedMethod.method.getParameterTypes();
|
|
||||||
|
|
||||||
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
|
|
||||||
|
// we don't start at 0 for the arguments, in case we have an overwritten method (in which case, the 1st arg is always "Connection.class")
|
||||||
for (int i = 0, n = serializers.length, j = argStartIndex; i < n; i++, j++) {
|
for (int i = 0, n = serializers.length, j = argStartIndex; i < n; i++, j++) {
|
||||||
Serializer<?> serializer = serializers[i];
|
Serializer<?> serializer = serializers[i];
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import com.esotericsoftware.kryo.Kryo;
|
||||||
import com.esotericsoftware.kryo.Serializer;
|
import com.esotericsoftware.kryo.Serializer;
|
||||||
import com.esotericsoftware.kryo.io.Input;
|
import com.esotericsoftware.kryo.io.Input;
|
||||||
import com.esotericsoftware.kryo.io.Output;
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
|
import com.esotericsoftware.kryo.util.IdentityMap;
|
||||||
|
|
||||||
import dorkbox.network.connection.KryoExtra;
|
import dorkbox.network.connection.KryoExtra;
|
||||||
|
|
||||||
|
@ -50,8 +51,12 @@ import dorkbox.network.connection.KryoExtra;
|
||||||
public
|
public
|
||||||
class RemoteObjectSerializer<T> extends Serializer<T> {
|
class RemoteObjectSerializer<T> extends Serializer<T> {
|
||||||
|
|
||||||
|
private final IdentityMap<Class<?>, Class<?>> rmiImplToIface;
|
||||||
|
|
||||||
public
|
public
|
||||||
RemoteObjectSerializer() {
|
RemoteObjectSerializer(final IdentityMap<Class<?>, Class<?>> rmiImplToIface) {
|
||||||
|
super(false);
|
||||||
|
this.rmiImplToIface = rmiImplToIface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,9 +70,13 @@ class RemoteObjectSerializer<T> extends Serializer<T> {
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
T read(Kryo kryo, Input input, Class type) {
|
T read(Kryo kryo, Input input, Class implementationType) {
|
||||||
KryoExtra kryoExtra = (KryoExtra) kryo;
|
KryoExtra kryoExtra = (KryoExtra) kryo;
|
||||||
int objectID = input.readInt(true);
|
int objectID = input.readInt(true);
|
||||||
return (T) kryoExtra.connection.getProxyObject(objectID, type);
|
|
||||||
|
// We have to lookup the iface, since the proxy object requires it
|
||||||
|
Class<?> iface = rmiImplToIface.get(implementationType);
|
||||||
|
|
||||||
|
return (T) kryoExtra.connection.getProxyObject(objectID, iface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ import dorkbox.util.collections.LockFreeIntBiMap;
|
||||||
* object transformation (because there is no serialization occurring) using a series of weak hashmaps.
|
* object transformation (because there is no serialization occurring) using a series of weak hashmaps.
|
||||||
* <p/>
|
* <p/>
|
||||||
* <p/>
|
* <p/>
|
||||||
* Objects are {@link RmiSerializationManager#registerRmiInterface(Class)}, and endpoint connections can then {@link
|
* Objects are {@link RmiSerializationManager#registerRmi(Class, Class)}, and endpoint connections can then {@link
|
||||||
* Connection#createRemoteObject(Class, RemoteObjectCallback)} for the registered objects.
|
* Connection#createRemoteObject(Class, RemoteObjectCallback)} for the registered objects.
|
||||||
* <p/>
|
* <p/>
|
||||||
* It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a return value which
|
* It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a return value which
|
||||||
|
@ -236,7 +236,7 @@ class RmiBridge {
|
||||||
.append(argString)
|
.append(argString)
|
||||||
.append(")");
|
.append(")");
|
||||||
|
|
||||||
if (cachedMethod.overriddenMethod) {
|
if (cachedMethod.overriddenMethod != null) {
|
||||||
// did we override our cached method? This is not common.
|
// did we override our cached method? This is not common.
|
||||||
stringBuilder.append(" [Connection method override]");
|
stringBuilder.append(" [Connection method override]");
|
||||||
}
|
}
|
||||||
|
@ -279,8 +279,8 @@ class RmiBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
InvokeMethodResult invokeMethodResult = new InvokeMethodResult();
|
InvokeMethodResult invokeMethodResult = new InvokeMethodResult();
|
||||||
invokeMethodResult.objectID = invokeMethod.objectID;
|
invokeMethodResult.rmiObjectId = invokeMethod.objectID;
|
||||||
invokeMethodResult.responseID = (byte) responseID;
|
invokeMethodResult.responseId = (byte) responseID;
|
||||||
|
|
||||||
|
|
||||||
// Do not return non-primitives if transmitReturnVal is false
|
// Do not return non-primitives if transmitReturnVal is false
|
||||||
|
|
|
@ -101,7 +101,7 @@ class RmiObjectLocalHandler extends RmiObjectHandler {
|
||||||
|
|
||||||
int argStartIndex;
|
int argStartIndex;
|
||||||
|
|
||||||
if (cachedMethod.overriddenMethod) {
|
if (cachedMethod.overriddenMethod != null) {
|
||||||
// did we override our cached method? This is not common.
|
// did we override our cached method? This is not common.
|
||||||
// this is specifically when we override an interface method, with an implementation method + Connection parameter (@ index 0)
|
// this is specifically when we override an interface method, with an implementation method + Connection parameter (@ index 0)
|
||||||
argStartIndex = 1;
|
argStartIndex = 1;
|
||||||
|
|
|
@ -54,8 +54,12 @@ class RmiObjectNetworkHandler extends RmiObjectHandler {
|
||||||
//
|
//
|
||||||
// CREATE a new ID, and register the ID and new object (must create a new one) in the object maps
|
// CREATE a new ID, and register the ID and new object (must create a new one) in the object maps
|
||||||
|
|
||||||
|
// have to lookup the implementation class
|
||||||
|
Class<?> rmiImpl = connection.getEndPoint().getSerialization().getRmiImpl(interfaceClass);
|
||||||
|
|
||||||
|
|
||||||
// For network connections, the interface class kryo ID == implementation class kryo ID, so they switch automatically.
|
// For network connections, the interface class kryo ID == implementation class kryo ID, so they switch automatically.
|
||||||
RmiRegistration registrationResult = connection.createNewRmiObject(interfaceClass, interfaceClass, callbackId);
|
RmiRegistration registrationResult = connection.createNewRmiObject(interfaceClass, rmiImpl, callbackId);
|
||||||
connection.send(registrationResult);
|
connection.send(registrationResult);
|
||||||
// connection transport is flushed in calling method (don't need to do it here)
|
// connection transport is flushed in calling method (don't need to do it here)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,6 @@ class RmiRegistration implements RmiMessage {
|
||||||
*/
|
*/
|
||||||
public int callbackId;
|
public int callbackId;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private
|
|
||||||
RmiRegistration() {
|
|
||||||
// for serialization
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When requesting a new or existing remote object
|
* When requesting a new or existing remote object
|
||||||
* SENT FROM "local" -> "remote"
|
* SENT FROM "local" -> "remote"
|
||||||
|
|
83
src/dorkbox/network/rmi/RmiRegistrationSerializer.java
Normal file
83
src/dorkbox/network/rmi/RmiRegistrationSerializer.java
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2010 dorkbox, llc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package dorkbox.network.rmi;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
|
import com.esotericsoftware.kryo.Serializer;
|
||||||
|
import com.esotericsoftware.kryo.io.Input;
|
||||||
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
|
|
||||||
|
import dorkbox.network.connection.KryoExtra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is required, because with RMI, it is possible that the IMPL and IFACE can have DIFFERENT class IDs, in which case, the "client" cannot read the correct
|
||||||
|
* objects (because the IMPL class might not be registered, or that ID might be registered to a different class)
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
class RmiRegistrationSerializer extends Serializer<RmiRegistration> {
|
||||||
|
|
||||||
|
public
|
||||||
|
RmiRegistrationSerializer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void write(Kryo kryo, Output output, RmiRegistration object) {
|
||||||
|
output.writeBoolean(object.isRequest);
|
||||||
|
output.writeInt(object.rmiId, true);
|
||||||
|
output.writeInt(object.callbackId, true);
|
||||||
|
|
||||||
|
int id = kryo.getRegistration(object.interfaceClass).getId();
|
||||||
|
output.writeInt(id, true);
|
||||||
|
|
||||||
|
if (object.remoteObject != null) {
|
||||||
|
KryoExtra kryoExtra = (KryoExtra) kryo;
|
||||||
|
id = kryoExtra.connection.getRegisteredId(object.remoteObject);
|
||||||
|
} else {
|
||||||
|
// can be < 0 or >= RmiBridge.INVALID_RMI (Integer.MAX_VALUE)
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
output.writeInt(id, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
RmiRegistration read(Kryo kryo, Input input, Class implementationType) {
|
||||||
|
|
||||||
|
boolean isRequest = input.readBoolean();
|
||||||
|
int rmiId = input.readInt(true);
|
||||||
|
int callbackId = input.readInt(true);
|
||||||
|
|
||||||
|
int interfaceClassId = input.readInt(true);
|
||||||
|
int remoteObjectId = input.readInt(false);
|
||||||
|
|
||||||
|
|
||||||
|
// // We have to lookup the iface, since the proxy object requires it
|
||||||
|
Class iface = kryo.getRegistration(interfaceClassId).getType();
|
||||||
|
|
||||||
|
Object remoteObject = null;
|
||||||
|
if (remoteObjectId >= 0 && remoteObjectId < RmiBridge.INVALID_RMI) {
|
||||||
|
KryoExtra kryoExtra = (KryoExtra) kryo;
|
||||||
|
remoteObject = kryoExtra.connection.getProxyObject(remoteObjectId, iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
RmiRegistration rmiRegistration = new RmiRegistration(iface, rmiId, callbackId, remoteObject);
|
||||||
|
rmiRegistration.isRequest = isRequest;
|
||||||
|
|
||||||
|
return rmiRegistration;
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,18 +100,13 @@ class RmiUtils {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param logger
|
|
||||||
* @param kryo
|
|
||||||
* @param asmEnabled
|
|
||||||
* @param iFace this is never null.
|
* @param iFace this is never null.
|
||||||
* @param impl this is NULL on the rmi "client" side. This is NOT NULL on the "server" side (where the object lives)
|
* @param impl this is NULL on the rmi "client" side. This is NOT NULL on the "server" side (where the object lives)
|
||||||
* @param classId
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
CachedMethod[] getCachedMethods(final Logger logger, final Kryo kryo, final boolean asmEnabled, final Class<?> iFace, final Class<?> impl, final int classId) {
|
CachedMethod[] getCachedMethods(final Logger logger, final Kryo kryo, final boolean asmEnabled, final Class<?> iFace, final Class<?> impl, final int classId) {
|
||||||
MethodAccess ifaceMethodAccess = null;
|
MethodAccess ifaceAsmMethodAccess = null;
|
||||||
MethodAccess implMethodAccess = null;
|
MethodAccess implAsmMethodAccess = null;
|
||||||
|
|
||||||
// RMI is **ALWAYS** based upon an interface, so we must always make sure to get the methods of the interface, instead of the
|
// RMI is **ALWAYS** based upon an interface, so we must always make sure to get the methods of the interface, instead of the
|
||||||
// implementation, otherwise we will have the wrong order of methods, so invoking a method by it's index will fail.
|
// implementation, otherwise we will have the wrong order of methods, so invoking a method by it's index will fail.
|
||||||
|
@ -120,22 +115,24 @@ class RmiUtils {
|
||||||
final int size = methods.length;
|
final int size = methods.length;
|
||||||
final CachedMethod[] cachedMethods = new CachedMethod[size];
|
final CachedMethod[] cachedMethods = new CachedMethod[size];
|
||||||
|
|
||||||
|
final Method[] implMethods;
|
||||||
|
|
||||||
if (impl != null) {
|
if (impl != null) {
|
||||||
if (impl.isInterface()) {
|
if (impl.isInterface()) {
|
||||||
throw new IllegalArgumentException("Cannot have type as an interface, it must be an implementation");
|
throw new IllegalArgumentException("Cannot have type as an interface, it must be an implementation");
|
||||||
}
|
}
|
||||||
|
|
||||||
final Method[] implMethods = getMethods(impl);
|
implMethods = getMethods(impl);
|
||||||
overwriteMethodsWithConnectionParam(implMethods, methods);
|
|
||||||
|
|
||||||
|
|
||||||
// reflectASM
|
// reflectASM
|
||||||
// doesn't work on android (set correctly by the serialization manager)
|
// doesn't work on android (set correctly by the serialization manager)
|
||||||
// can't get any method from the 'Object' object (we get from the interface, which is NOT 'Object')
|
// can't get any method from the 'Object' object (we get from the interface, which is NOT 'Object')
|
||||||
// and it MUST be public (iFace is always public)
|
// and it MUST be public (iFace is always public)
|
||||||
if (asmEnabled) {
|
if (asmEnabled) {
|
||||||
implMethodAccess = getReflectAsmMethod(logger, impl);
|
implAsmMethodAccess = getReflectAsmMethod(logger, impl);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
implMethods = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reflectASM
|
// reflectASM
|
||||||
|
@ -143,7 +140,7 @@ class RmiUtils {
|
||||||
// can't get any method from the 'Object' object (we get from the interface, which is NOT 'Object')
|
// can't get any method from the 'Object' object (we get from the interface, which is NOT 'Object')
|
||||||
// and it MUST be public (iFace is always public)
|
// and it MUST be public (iFace is always public)
|
||||||
if (asmEnabled) {
|
if (asmEnabled) {
|
||||||
ifaceMethodAccess = getReflectAsmMethod(logger, iFace);
|
ifaceAsmMethodAccess = getReflectAsmMethod(logger, iFace);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
|
@ -153,50 +150,44 @@ class RmiUtils {
|
||||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
|
|
||||||
// copy because they can be overridden
|
// copy because they can be overridden
|
||||||
boolean overriddenMethod = false;
|
CachedMethod cachedMethod = null;
|
||||||
MethodAccess tweakMethodAccess = ifaceMethodAccess;
|
MethodAccess ifaceORimplMethodAccess = ifaceAsmMethodAccess;
|
||||||
|
|
||||||
|
// reflectAsm doesn't like "Object" class methods
|
||||||
|
boolean canUseAsm = asmEnabled && method.getDeclaringClass() != Object.class;
|
||||||
|
|
||||||
|
Method overwrittenMethod = null;
|
||||||
|
|
||||||
// this is how we detect if the method has been changed from the interface -> implementation + connection parameter
|
// this is how we detect if the method has been changed from the interface -> implementation + connection parameter
|
||||||
if (declaringClass.equals(impl)) {
|
if (implMethods != null) {
|
||||||
tweakMethodAccess = implMethodAccess;
|
overwrittenMethod = getOverwriteMethodWithConnectionParam(implMethods, method);
|
||||||
overriddenMethod = true;
|
if (overwrittenMethod != null) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
|
logger.trace("Overridden method: {}.{}", impl, method.getName());
|
||||||
|
}
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
// still might be null!
|
||||||
logger.trace("Overridden method: {}.{}", impl, method.getName());
|
ifaceORimplMethodAccess = implAsmMethodAccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CachedMethod cachedMethod = null;
|
if (canUseAsm) {
|
||||||
// reflectAsm doesn't like "Object" class methods...
|
|
||||||
if (tweakMethodAccess != null && method.getDeclaringClass() != Object.class) {
|
|
||||||
try {
|
try {
|
||||||
final int index = tweakMethodAccess.getIndex(method.getName(), parameterTypes);
|
int index;
|
||||||
|
if (overwrittenMethod != null) {
|
||||||
|
// have to take into account the overwritten method's first parameter will ALWAYS be "Connection"
|
||||||
|
index = ifaceORimplMethodAccess.getIndex(method.getName(), overwrittenMethod.getParameterTypes());
|
||||||
|
} else {
|
||||||
|
index = ifaceORimplMethodAccess.getIndex(method.getName(), parameterTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
AsmCachedMethod asmCachedMethod = new AsmCachedMethod();
|
AsmCachedMethod asmCachedMethod = new AsmCachedMethod();
|
||||||
asmCachedMethod.methodAccessIndex = index;
|
asmCachedMethod.methodAccessIndex = index;
|
||||||
asmCachedMethod.methodAccess = tweakMethodAccess;
|
asmCachedMethod.methodAccess = ifaceORimplMethodAccess;
|
||||||
asmCachedMethod.name = method.getName();
|
asmCachedMethod.name = method.getName();
|
||||||
|
|
||||||
if (overriddenMethod) {
|
|
||||||
// logger.error(tweakMethod.getName() + " " + Arrays.toString(parameterTypes) + " index: " + index +
|
|
||||||
// " methodIndex: " + i + " classID: " + classId);
|
|
||||||
|
|
||||||
// This is because we have to store the serializer for each parameter, but ONLY for the ORIGINAL method, not the overridden one.
|
|
||||||
// this gets our parameters "back to the original" method. We do this to minimize the overhead of sending the args over
|
|
||||||
int length = parameterTypes.length;
|
|
||||||
if (length == 1) {
|
|
||||||
parameterTypes = new Class<?>[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
length--;
|
|
||||||
Class<?>[] newArgs = new Class<?>[length];
|
|
||||||
System.arraycopy(parameterTypes, 1, newArgs, 0, length);
|
|
||||||
parameterTypes = newArgs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedMethod = asmCachedMethod;
|
cachedMethod = asmCachedMethod;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.trace("Unable to use ReflectAsm for {}.{} (using java reflection instead)", declaringClass, method.getName(), e);
|
logger.trace("Unable to use ReflectAsm for {}.{} (using java reflection instead)", declaringClass, method.getName(), e);
|
||||||
|
@ -207,10 +198,11 @@ class RmiUtils {
|
||||||
cachedMethod = new CachedMethod();
|
cachedMethod = new CachedMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedMethod.overriddenMethod = overriddenMethod;
|
|
||||||
cachedMethod.methodClassID = classId;
|
cachedMethod.methodClassID = classId;
|
||||||
|
|
||||||
// we ALSO have to setup "normal" reflection access to these methods
|
// this MIGHT be null, but if it is not, this is the method we will invoke INSTEAD of the "normal" method
|
||||||
|
cachedMethod.overriddenMethod = overwrittenMethod;
|
||||||
cachedMethod.method = method;
|
cachedMethod.method = method;
|
||||||
cachedMethod.methodIndex = i;
|
cachedMethod.methodIndex = i;
|
||||||
|
|
||||||
|
@ -229,9 +221,9 @@ class RmiUtils {
|
||||||
return cachedMethods;
|
return cachedMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: does not null check
|
|
||||||
/**
|
/**
|
||||||
* This will overwrite an original (iface based) method with a method from the implementation ONLY if there is the extra 'Connection' parameter (as per above)
|
* This will overwrite an original (iface based) method with a method from the implementation ONLY if there is the extra 'Connection' parameter (as per above)
|
||||||
|
* NOTE: does not null check
|
||||||
*
|
*
|
||||||
* @param implMethods methods from the implementation
|
* @param implMethods methods from the implementation
|
||||||
* @param origMethods methods from the interface
|
* @param origMethods methods from the interface
|
||||||
|
@ -241,47 +233,62 @@ class RmiUtils {
|
||||||
for (int i = 0, origMethodsSize = origMethods.length; i < origMethodsSize; i++) {
|
for (int i = 0, origMethodsSize = origMethods.length; i < origMethodsSize; i++) {
|
||||||
final Method origMethod = origMethods[i];
|
final Method origMethod = origMethods[i];
|
||||||
|
|
||||||
String origName = origMethod.getName();
|
Method overwriteMethodsWithConnectionParam = getOverwriteMethodWithConnectionParam(implMethods, origMethod);
|
||||||
Class<?>[] origTypes = origMethod.getParameterTypes();
|
if (overwriteMethodsWithConnectionParam != null) {
|
||||||
int origLength = origTypes.length + 1;
|
origMethods[i] = overwriteMethodsWithConnectionParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (Method implMethod : implMethods) {
|
/**
|
||||||
String implName = implMethod.getName();
|
* This will overwrite an original (iface based) method with a method from the implementation ONLY if there is the extra 'Connection' parameter (as per above)
|
||||||
Class<?>[] implTypes = implMethod.getParameterTypes();
|
* NOTE: does not null check
|
||||||
int implLength = implTypes.length;
|
*
|
||||||
|
* @param implMethods methods from the implementation
|
||||||
|
* @param origMethod original method from the interface
|
||||||
|
*/
|
||||||
|
private static
|
||||||
|
Method getOverwriteMethodWithConnectionParam(final Method[] implMethods, final Method origMethod) {
|
||||||
|
String origName = origMethod.getName();
|
||||||
|
Class<?>[] origTypes = origMethod.getParameterTypes();
|
||||||
|
int origLength = origTypes.length + 1;
|
||||||
|
|
||||||
if (origLength != implLength || !(origName.equals(implName))) {
|
for (Method implMethod : implMethods) {
|
||||||
continue;
|
String implName = implMethod.getName();
|
||||||
|
Class<?>[] implTypes = implMethod.getParameterTypes();
|
||||||
|
int implLength = implTypes.length;
|
||||||
|
|
||||||
|
if (origLength != implLength || !(origName.equals(implName))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkLength > 0
|
||||||
|
Class<?> shouldBeConnectionType = implTypes[0];
|
||||||
|
if (ClassHelper.hasInterface(Connection.class, shouldBeConnectionType)) {
|
||||||
|
// now we check to see if our "check" method is equal to our "cached" method + Connection
|
||||||
|
if (implLength == 1) {
|
||||||
|
// we only have "Connection" as a parameter
|
||||||
|
return implMethod;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// checkLength > 0
|
boolean found = true;
|
||||||
Class<?> shouldBeConnectionType = implTypes[0];
|
for (int k = 1; k < implLength; k++) {
|
||||||
if (ClassHelper.hasInterface(Connection.class, shouldBeConnectionType)) {
|
if (origTypes[k - 1] != implTypes[k]) {
|
||||||
// now we check to see if our "check" method is equal to our "cached" method + Connection
|
// make sure all the parameters match. Cannot use arrays.equals(*), because one will have "Connection" as
|
||||||
if (implLength == 1) {
|
// a parameter - so we check that the rest match
|
||||||
// we only have "Connection" as a parameter
|
found = false;
|
||||||
origMethods[i] = implMethod;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
boolean found = true;
|
|
||||||
for (int k = 1; k < implLength; k++) {
|
|
||||||
if (origTypes[k - 1] != implTypes[k]) {
|
|
||||||
// make sure all the parameters match. Cannot use arrays.equals(*), because one will have "Connection" as
|
|
||||||
// a parameter - so we check that the rest match
|
|
||||||
found = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
origMethods[i] = implMethod;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return implMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -103,30 +103,37 @@ interface RmiSerializationManager extends SerializationManager {
|
||||||
Class<?> getRmiImpl(Class<?> iFace);
|
Class<?> getRmiImpl(Class<?> iFace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable remote method invocation (RMI) for this connection. There is additional overhead to using RMI.
|
* Enable this endpoint (a "client") to access methods and create objects (RMI) on a "remote server". This is NOT bi-directional, and the "remote server"
|
||||||
|
* cannot access or create remote objects on this endpoint.
|
||||||
|
* <p>
|
||||||
|
* This is the same as calling {@link RmiSerializationManager#registerRmi(Class, Class)} with a null parameter for the implementation class.
|
||||||
|
* <p>
|
||||||
|
* There is additional overhead to using RMI.
|
||||||
* <p>
|
* <p>
|
||||||
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
||||||
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
||||||
* <p>
|
* <p>
|
||||||
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
||||||
*/
|
*/
|
||||||
RmiSerializationManager registerRmiInterface(Class<?> ifaceClass);
|
RmiSerializationManager registerRmi(Class<?> ifaceClass);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable remote method invocation (RMI) for this connection. There is additional overhead to using RMI.
|
* Enable a "remote client" 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".
|
||||||
|
* <p>
|
||||||
|
* Calling this method with a null parameter for the implementation class is the same as calling {@link RmiSerializationManager#registerRmi(Class)}
|
||||||
|
* <p>
|
||||||
|
* There is additional overhead to using RMI.
|
||||||
* <p>
|
* <p>
|
||||||
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
||||||
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
||||||
* <p>
|
* <p>
|
||||||
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
||||||
* <p>
|
* <p>
|
||||||
* This method overrides the interface -> implementation. This is so incoming proxy objects will get auto-changed into their correct
|
|
||||||
* implementation type, so this side of the connection knows what to do with the proxy object.
|
|
||||||
* <p>
|
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the iface/impl have previously been overridden
|
* @throws IllegalArgumentException if the iface/impl have previously been overridden
|
||||||
*/
|
*/
|
||||||
<Iface, Impl extends Iface> RmiSerializationManager registerRmiImplementation(Class<Iface> ifaceClass, Class<Impl> implClass);
|
<Iface, Impl extends Iface> RmiSerializationManager registerRmi(Class<Iface> ifaceClass, Class<Impl> implClass);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the cached methods for the specified class ID
|
* Gets the cached methods for the specified class ID
|
||||||
|
|
|
@ -31,7 +31,6 @@ import org.slf4j.Logger;
|
||||||
import com.esotericsoftware.kryo.ClassResolver;
|
import com.esotericsoftware.kryo.ClassResolver;
|
||||||
import com.esotericsoftware.kryo.Kryo;
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
import com.esotericsoftware.kryo.KryoException;
|
import com.esotericsoftware.kryo.KryoException;
|
||||||
import com.esotericsoftware.kryo.Registration;
|
|
||||||
import com.esotericsoftware.kryo.Serializer;
|
import com.esotericsoftware.kryo.Serializer;
|
||||||
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory;
|
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory;
|
||||||
import com.esotericsoftware.kryo.factories.SerializerFactory;
|
import com.esotericsoftware.kryo.factories.SerializerFactory;
|
||||||
|
@ -55,6 +54,7 @@ import dorkbox.network.rmi.InvokeMethodResult;
|
||||||
import dorkbox.network.rmi.InvokeMethodSerializer;
|
import dorkbox.network.rmi.InvokeMethodSerializer;
|
||||||
import dorkbox.network.rmi.RemoteObjectSerializer;
|
import dorkbox.network.rmi.RemoteObjectSerializer;
|
||||||
import dorkbox.network.rmi.RmiRegistration;
|
import dorkbox.network.rmi.RmiRegistration;
|
||||||
|
import dorkbox.network.rmi.RmiRegistrationSerializer;
|
||||||
import dorkbox.network.rmi.RmiUtils;
|
import dorkbox.network.rmi.RmiUtils;
|
||||||
import dorkbox.objectPool.ObjectPool;
|
import dorkbox.objectPool.ObjectPool;
|
||||||
import dorkbox.objectPool.PoolableObject;
|
import dorkbox.objectPool.PoolableObject;
|
||||||
|
@ -127,23 +127,12 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static
|
private static
|
||||||
class RemoteIfaceClass {
|
class RmiClassSerializer {
|
||||||
private final Class<?> ifaceClass;
|
|
||||||
|
|
||||||
RemoteIfaceClass(final Class<?> ifaceClass) {
|
|
||||||
this.ifaceClass = ifaceClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static
|
|
||||||
class RemoteImplClass {
|
|
||||||
private final Class<?> ifaceClass;
|
private final Class<?> ifaceClass;
|
||||||
private final Class<?> implClass;
|
private final Class<?> implClass;
|
||||||
|
|
||||||
RemoteImplClass(final Class<?> ifaceClass, final Class<?> implClass) {
|
RmiClassSerializer(final Class<?> ifaceClass, final Class<?> implClass) {
|
||||||
this.ifaceClass = ifaceClass;
|
this.ifaceClass = ifaceClass;
|
||||||
this.implClass = implClass;
|
this.implClass = implClass;
|
||||||
}
|
}
|
||||||
|
@ -171,8 +160,8 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
* <p>
|
* <p>
|
||||||
* Registered classes are serialized as an int id, avoiding the overhead of serializing the class name, but have the
|
* Registered classes are serialized as an int id, avoiding the overhead of serializing the class name, but have the
|
||||||
* drawback of needing to know the classes to be serialized up front.
|
* drawback of needing to know the classes to be serialized up front.
|
||||||
* @param implementationRequired If true, interfaces are not permitted to be registered, outside of the {@link #registerRmiInterface(Class)} and
|
* @param implementationRequired If true, interfaces are not permitted to be registered, outside of the {@link #registerRmi(Class, Class)} and
|
||||||
* {@link #registerRmiImplementation(Class, Class)} methods. If false, then interfaces can also be registered.
|
* {@link #registerRmi(Class, Class)} methods. If false, then interfaces can also be registered.
|
||||||
* <p>
|
* <p>
|
||||||
* Enabling interface registration permits matching a different RMI client/server serialization scheme, since
|
* Enabling interface registration permits matching a different RMI client/server serialization scheme, since
|
||||||
* interfaces are generally in a "common" package, accessible to both the RMI client and server.
|
* interfaces are generally in a "common" package, accessible to both the RMI client and server.
|
||||||
|
@ -263,10 +252,7 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
// the purpose of the method cache, is to accelerate looking up methods for specific class
|
// the purpose of the method cache, is to accelerate looking up methods for specific class
|
||||||
private IntMap<CachedMethod[]> methodCache;
|
private IntMap<CachedMethod[]> methodCache;
|
||||||
private RemoteObjectSerializer remoteObjectSerializer;
|
private RemoteObjectSerializer remoteObjectSerializer;
|
||||||
|
private RmiRegistrationSerializer rmiRegistrationSerializer;
|
||||||
// used to track which interface -> implementation, for use by RMI
|
|
||||||
private final IntMap<Class<?>> rmiKryoIdToImpl = new IntMap<Class<?>>();
|
|
||||||
private final IntMap<Class<?>> rmiKryoIdToIface = new IntMap<Class<?>>();
|
|
||||||
|
|
||||||
private final IdentityMap<Class<?>, Class<?>> rmiIfaceToImpl = new IdentityMap<Class<?>, Class<?>>();
|
private final IdentityMap<Class<?>, Class<?>> rmiIfaceToImpl = new IdentityMap<Class<?>, Class<?>>();
|
||||||
private final IdentityMap<Class<?>, Class<?>> rmiImplToIface = new IdentityMap<Class<?>, Class<?>>();
|
private final IdentityMap<Class<?>, Class<?>> rmiImplToIface = new IdentityMap<Class<?>, Class<?>>();
|
||||||
|
@ -296,8 +282,8 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
* Registered classes are serialized as an int id, avoiding the overhead of serializing the class name, but have the
|
* Registered classes are serialized as an int id, avoiding the overhead of serializing the class name, but have the
|
||||||
* drawback of needing to know the classes to be serialized up front.
|
* drawback of needing to know the classes to be serialized up front.
|
||||||
* <p>
|
* <p>
|
||||||
* @param implementationRequired If true, interfaces are not permitted to be registered, outside of the {@link #registerRmiInterface(Class)} and
|
* @param implementationRequired If true, interfaces are not permitted to be registered, outside of the {@link #registerRmi(Class, Class)} and
|
||||||
* {@link #registerRmiImplementation(Class, Class)} methods. If false, then interfaces can also be registered.
|
* {@link #registerRmi(Class, Class)} methods. If false, then interfaces can also be registered.
|
||||||
* <p>
|
* <p>
|
||||||
* Enabling interface registration permits matching a different RMI client/server serialization scheme, since
|
* Enabling interface registration permits matching a different RMI client/server serialization scheme, since
|
||||||
* interfaces are generally in a "common" package, accessible to both the RMI client and server.
|
* interfaces are generally in a "common" package, accessible to both the RMI client and server.
|
||||||
|
@ -325,6 +311,8 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
// we HAVE to pre-allocate the KRYOs
|
// we HAVE to pre-allocate the KRYOs
|
||||||
KryoExtra<C> kryo = new KryoExtra<C>(Serialization.this);
|
KryoExtra<C> kryo = new KryoExtra<C>(Serialization.this);
|
||||||
|
|
||||||
|
boolean traceEnabled = logger.isTraceEnabled();
|
||||||
|
|
||||||
kryo.getFieldSerializerConfig()
|
kryo.getFieldSerializerConfig()
|
||||||
.setUseAsm(useAsm);
|
.setUseAsm(useAsm);
|
||||||
kryo.setRegistrationRequired(registrationRequired);
|
kryo.setRegistrationRequired(registrationRequired);
|
||||||
|
@ -333,13 +321,13 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
|
|
||||||
if (usesRmi) {
|
if (usesRmi) {
|
||||||
kryo.register(Class.class);
|
kryo.register(Class.class);
|
||||||
kryo.register(RmiRegistration.class);
|
kryo.register(RmiRegistration.class, rmiRegistrationSerializer);
|
||||||
kryo.register(InvokeMethod.class, methodSerializer);
|
kryo.register(InvokeMethod.class, methodSerializer);
|
||||||
kryo.register(Object[].class);
|
kryo.register(Object[].class);
|
||||||
|
|
||||||
// This has to be for each kryo instance!
|
// This has to be for each kryo instance!
|
||||||
InvocationResultSerializer resultSerializer = new InvocationResultSerializer(kryo);
|
InvocationResultSerializer resultSerializer = new InvocationResultSerializer(kryo);
|
||||||
resultSerializer.removeField("objectID");
|
resultSerializer.removeField("rmiObjectId");
|
||||||
|
|
||||||
kryo.register(InvokeMethodResult.class, resultSerializer);
|
kryo.register(InvokeMethodResult.class, resultSerializer);
|
||||||
kryo.register(InvocationHandler.class, invocationSerializer);
|
kryo.register(InvocationHandler.class, invocationSerializer);
|
||||||
|
@ -349,63 +337,56 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
for (Object clazz : classesToRegister) {
|
for (Object clazz : classesToRegister) {
|
||||||
if (clazz instanceof Class) {
|
if (clazz instanceof Class) {
|
||||||
Class aClass = (Class) clazz;
|
Class aClass = (Class) clazz;
|
||||||
kryo.register(aClass);
|
int id = kryo.register(aClass).getId();
|
||||||
|
|
||||||
|
if (traceEnabled) {
|
||||||
|
logger.trace("Registering {} -> {}", id, aClass.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (clazz instanceof ClassSerializer) {
|
else if (clazz instanceof ClassSerializer) {
|
||||||
ClassSerializer classSerializer = (ClassSerializer) clazz;
|
ClassSerializer classSerializer = (ClassSerializer) clazz;
|
||||||
kryo.register(classSerializer.clazz, classSerializer.serializer);
|
int id = kryo.register(classSerializer.clazz, classSerializer.serializer).getId();
|
||||||
|
|
||||||
|
if (traceEnabled) {
|
||||||
|
logger.trace("Registering {} -> {} using {}", id, classSerializer.clazz.getName(), classSerializer.serializer.getClass().getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (clazz instanceof ClassSerializer1) {
|
else if (clazz instanceof ClassSerializer1) {
|
||||||
ClassSerializer1 classSerializer = (ClassSerializer1) clazz;
|
ClassSerializer1 classSerializer = (ClassSerializer1) clazz;
|
||||||
kryo.register(classSerializer.clazz, classSerializer.id);
|
kryo.register(classSerializer.clazz, classSerializer.id);
|
||||||
|
|
||||||
|
if (traceEnabled) {
|
||||||
|
logger.trace("Registering {} (specified) -> {}", classSerializer.id, classSerializer.clazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (clazz instanceof ClassSerializer2) {
|
else if (clazz instanceof ClassSerializer2) {
|
||||||
ClassSerializer2 classSerializer = (ClassSerializer2) clazz;
|
ClassSerializer2 classSerializer = (ClassSerializer2) clazz;
|
||||||
kryo.register(classSerializer.clazz, classSerializer.serializer, classSerializer.id);
|
kryo.register(classSerializer.clazz, classSerializer.serializer, classSerializer.id);
|
||||||
}
|
|
||||||
else if (clazz instanceof RemoteIfaceClass) {
|
|
||||||
// THIS IS DONE ON THE "CLIENT"
|
|
||||||
// "server" means the side of the connection that has the implementation details for the RMI object
|
|
||||||
// "client" means the side of the connection that accesses the "server" side object via a proxy object
|
|
||||||
// the client will NEVER send this object to the server.
|
|
||||||
// the server will ONLY send this object to the client, where on the client it becomes the proxy/interface.
|
|
||||||
RemoteIfaceClass remoteIfaceClass = (RemoteIfaceClass) clazz;
|
|
||||||
|
|
||||||
// registers the interface, so that when it is READ, it becomes a "magic" proxy object
|
if (traceEnabled) {
|
||||||
kryo.register(remoteIfaceClass.ifaceClass, remoteObjectSerializer);
|
logger.trace("Registering {} (specified) -> {} using {}", classSerializer.id, classSerializer.clazz.getName(), classSerializer.serializer.getClass().getName());
|
||||||
}
|
|
||||||
else if (clazz instanceof RemoteImplClass) {
|
|
||||||
// THIS IS DONE ON THE "SERVER"
|
|
||||||
// "server" means the side of the connection that has the implementation details for the RMI object
|
|
||||||
// "client" means the side of the connection that accesses the "server" side object via a proxy object
|
|
||||||
// the client will NEVER send this object to the server.
|
|
||||||
// the server will ONLY send this object to the client, where on the client it becomes the proxy/interface.
|
|
||||||
RemoteImplClass remoteImplClass = (RemoteImplClass) clazz;
|
|
||||||
|
|
||||||
int id;
|
|
||||||
|
|
||||||
// check to see if the interface is already registered. If so, we override it with the implementation class
|
|
||||||
EditableDefaultClassResolver classResolver = (EditableDefaultClassResolver) kryo.getClassResolver();
|
|
||||||
Registration registration = classResolver.getRegistration(remoteImplClass.ifaceClass);
|
|
||||||
if (registration != null) {
|
|
||||||
// override it with the implementation
|
|
||||||
int oldId = registration.getId();
|
|
||||||
id = kryo.register(remoteImplClass.implClass, remoteObjectSerializer, oldId).getId();
|
|
||||||
|
|
||||||
// delete the old one (since it shouldn't be stored anywhere)
|
|
||||||
classResolver.deleteRegistrationType(remoteImplClass.ifaceClass);
|
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
// we have a new registration.
|
else if (clazz instanceof RmiClassSerializer) {
|
||||||
|
// "client" always sends kryo-ID (of iface) with RMI-ID (or command to create/get one)
|
||||||
|
// "server" always responds with kryo-ID (of impl) with RMI-ID of remote object. When creating proxy object, "client" looks-up kryo-id of iface from impl
|
||||||
|
|
||||||
// registers the implementation, so that when it is WRITTEN, it becomes a "magic" proxy object
|
RmiClassSerializer remoteImplClass = (RmiClassSerializer) clazz;
|
||||||
id = kryo.register(remoteImplClass.implClass, remoteObjectSerializer).getId();
|
|
||||||
|
int id = kryo.register(remoteImplClass.ifaceClass, remoteObjectSerializer).getId();
|
||||||
|
if (traceEnabled) {
|
||||||
|
logger.trace("Registering {} -> RMI interface : {}", id, remoteImplClass.ifaceClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets up the RMI, so when we receive the iface class from the client, we know what impl to use
|
// we only want to use an implementation class IFF we plan on having one, otherwise ignore it
|
||||||
// if this is over-written, we don't care.
|
if (remoteImplClass.implClass != null) {
|
||||||
rmiKryoIdToImpl.put(id, remoteImplClass.implClass); // the "server" translates the ID back to the impl on kryo read
|
// NOTE: as a "server" we must have the "remote object" serializer write out the INTERFACE CLASS ID so that the "client" (if it DOES NOT have an impl)
|
||||||
rmiKryoIdToIface.put(id, remoteImplClass.ifaceClass); // the "server" translates the ID to the iface on kryo write
|
// can read this as the correct class!
|
||||||
|
int id2 = kryo.register(remoteImplClass.implClass, remoteObjectSerializer).getId();
|
||||||
|
if (traceEnabled) {
|
||||||
|
logger.trace("Registering {} -> RMI implementation: {}", id2, remoteImplClass.implClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +549,8 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
|
|
||||||
methodSerializer = new InvokeMethodSerializer();
|
methodSerializer = new InvokeMethodSerializer();
|
||||||
invocationSerializer = new InvocationHandlerSerializer(logger);
|
invocationSerializer = new InvocationHandlerSerializer(logger);
|
||||||
remoteObjectSerializer = new RemoteObjectSerializer();
|
remoteObjectSerializer = new RemoteObjectSerializer(rmiImplToIface);
|
||||||
|
rmiRegistrationSerializer = new RmiRegistrationSerializer();
|
||||||
methodCache = new IntMap<CachedMethod[]>();
|
methodCache = new IntMap<CachedMethod[]>();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -604,7 +586,12 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable remote method invocation (RMI) for this connection. There is additional overhead to using RMI.
|
* Enable this endpoint (a "client") to access methods and create objects (RMI) on a "remote server". This is NOT bi-directional, and the "remote server"
|
||||||
|
* cannot access or create remote objects on this endpoint.
|
||||||
|
* <p>
|
||||||
|
* This is the same as calling {@link RmiSerializationManager#registerRmi(Class, Class)} with a null parameter for the implementation class.
|
||||||
|
* <p>
|
||||||
|
* There is additional overhead to using RMI.
|
||||||
* <p>
|
* <p>
|
||||||
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
||||||
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
||||||
|
@ -613,67 +600,120 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized
|
public synchronized
|
||||||
RmiSerializationManager registerRmiInterface(Class<?> ifaceClass) {
|
RmiSerializationManager registerRmi(Class<?> ifaceClass) {
|
||||||
if (initialized) {
|
return registerRmi(ifaceClass, null);
|
||||||
logger.warn("Serialization manager already initialized. Ignoring duplicate registerRemote(Class) call.");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (!ifaceClass.isInterface()) {
|
|
||||||
throw new IllegalArgumentException("Cannot register an implementation for RMI access. It must be an interface.");
|
|
||||||
}
|
|
||||||
|
|
||||||
usesRmi = true;
|
|
||||||
classesToRegister.add(new RemoteIfaceClass(ifaceClass));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable remote method invocation (RMI) for this connection. There is additional overhead to using RMI.
|
* Enable a "remote client" 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".
|
||||||
|
* <p>
|
||||||
|
* Calling this method with a null parameter for the implementation class is the same as calling {@link RmiSerializationManager#registerRmi(Class)}
|
||||||
|
* <p>
|
||||||
|
* There is additional overhead to using RMI.
|
||||||
* <p>
|
* <p>
|
||||||
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
* Specifically, It costs at least 2 bytes more to use remote method invocation than just sending the parameters. If the method has a
|
||||||
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
* return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, an extra byte is written.
|
||||||
* <p>
|
* <p>
|
||||||
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
* If the type of a parameter is not final (primitives are final) then an extra byte is written for that parameter.
|
||||||
* <p>
|
* <p>
|
||||||
* This method overrides the interface -> implementation. This is so incoming proxy objects will get auto-changed into their correct
|
|
||||||
* implementation type, so this side of the connection knows what to do with the proxy object.
|
|
||||||
* <p>
|
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the iface/impl have previously been overridden
|
* @throws IllegalArgumentException if the iface/impl have previously been overridden
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized
|
public synchronized
|
||||||
<Iface, Impl extends Iface> RmiSerializationManager registerRmiImplementation(Class<Iface> ifaceClass, Class<Impl> implClass) {
|
<Iface, Impl extends Iface> RmiSerializationManager registerRmi(Class<Iface> ifaceClass, Class<Impl> implClass) {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
logger.warn("Serialization manager already initialized. Ignoring duplicate registerRemote(Class, Class) call.");
|
logger.warn("Serialization manager already initialized. Ignoring duplicate registerRmiImplementation(Class, Class) call.");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ifaceClass.isInterface()) {
|
if (!ifaceClass.isInterface()) {
|
||||||
throw new IllegalArgumentException("Cannot register an implementation for RMI access. It must be an interface.");
|
throw new IllegalArgumentException("Cannot register an implementation for RMI access. It must be an interface.");
|
||||||
}
|
}
|
||||||
if (implClass.isInterface()) {
|
if (implClass != null && implClass.isInterface()) {
|
||||||
throw new IllegalArgumentException("Cannot register an interface for RMI implementations. It must be an implementation.");
|
throw new IllegalArgumentException("Cannot register an interface for RMI implementations. It must be an implementation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
usesRmi = true;
|
usesRmi = true;
|
||||||
classesToRegister.add(new RemoteImplClass(ifaceClass, implClass));
|
classesToRegister.add(new RmiClassSerializer(ifaceClass, implClass));
|
||||||
|
|
||||||
// this MUST BE UNIQUE otherwise unexpected and BAD things can happen.
|
// we only want to assign an implementation class IFF we plan on having one, otherwise ignore it
|
||||||
Class<?> a = this.rmiIfaceToImpl.put(ifaceClass, implClass);
|
if (implClass != null) {
|
||||||
Class<?> b = this.rmiImplToIface.put(implClass, ifaceClass);
|
// rmiIfaceToImpl tells the "server" how to create a (requested) remote object
|
||||||
|
// this MUST BE UNIQUE otherwise unexpected and BAD things can happen.
|
||||||
|
Class<?> a = this.rmiIfaceToImpl.put(ifaceClass, implClass);
|
||||||
|
Class<?> b = this.rmiImplToIface.put(implClass, ifaceClass);
|
||||||
|
|
||||||
if (a != null || b != null) {
|
if (a != null || b != null) {
|
||||||
throw new IllegalArgumentException("Unable to override interface (" + ifaceClass + ") and implementation (" + implClass + ") " +
|
throw new IllegalArgumentException("Unable to override interface (" + ifaceClass + ") and implementation (" + implClass + ") " +
|
||||||
"because they have already been overridden by something else. It is critical that they are" +
|
"because they have already been overridden by something else. It is critical that they are" +
|
||||||
" both unique per JVM");
|
" both unique per JVM");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when initialization is complete. This is to prevent (and recognize) out-of-order class/serializer registration. If an ID
|
||||||
|
* is already in use by a different type, a {@link KryoException} is thrown.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized
|
||||||
|
void finishInit(final Logger wireReadLogger, final Logger wireWriteLogger) {
|
||||||
|
this.wireReadLogger = wireReadLogger;
|
||||||
|
this.wireWriteLogger = wireWriteLogger;
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
// initialize the kryo pool with at least 1 kryo instance. This ALSO makes sure that all of our class registration is done
|
||||||
|
// correctly and (if not) we are are notified on the initial thread (instead of on the network update thread)
|
||||||
|
KryoExtra<C> kryo = null;
|
||||||
|
try {
|
||||||
|
kryo = kryoPool.take();
|
||||||
|
|
||||||
|
ClassResolver classResolver = kryo.getClassResolver();
|
||||||
|
|
||||||
|
// now initialize the RMI cached methods, so that they are "final" when the network threads need access to it.
|
||||||
|
for (Object clazz : classesToRegister) {
|
||||||
|
if (clazz instanceof RmiClassSerializer) {
|
||||||
|
// THIS IS DONE ON THE "SERVER"
|
||||||
|
// "server" means the side of the connection that has the implementation details for the RMI object
|
||||||
|
// "client" means the side of the connection that accesses the "server" side object via a proxy object
|
||||||
|
// the client will NEVER send this object to the server.
|
||||||
|
// the server will ONLY send this object to the client, where on the client it becomes the proxy/interface.
|
||||||
|
RmiClassSerializer remoteImplClass = (RmiClassSerializer) clazz;
|
||||||
|
int id = classResolver.getRegistration(remoteImplClass.ifaceClass)
|
||||||
|
.getId();
|
||||||
|
|
||||||
|
CachedMethod[] cachedMethods = RmiUtils.getCachedMethods(Serialization.logger, kryo, useAsm,
|
||||||
|
remoteImplClass.ifaceClass,
|
||||||
|
remoteImplClass.implClass,
|
||||||
|
id);
|
||||||
|
methodCache.put(id, cachedMethods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (kryo != null) {
|
||||||
|
kryoPool.put(kryo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
CachedMethod[] getMethods(final int classId) {
|
||||||
|
return methodCache.get(classId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized
|
||||||
|
boolean initialized() {
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until a kryo is available to write, using CAS operations to prevent having to synchronize.
|
* Waits until a kryo is available to write, using CAS operations to prevent having to synchronize.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -780,81 +820,6 @@ class Serialization<C extends CryptoConnection> implements CryptoSerializationMa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when initialization is complete. This is to prevent (and recognize) out-of-order class/serializer registration. If an ID
|
|
||||||
* is already in use by a different type, a {@link KryoException} is thrown.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public synchronized
|
|
||||||
void finishInit(final Logger wireReadLogger, final Logger wireWriteLogger) {
|
|
||||||
this.wireReadLogger = wireReadLogger;
|
|
||||||
this.wireWriteLogger = wireWriteLogger;
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
// initialize the kryo pool with at least 1 kryo instance. This ALSO makes sure that all of our class registration is done
|
|
||||||
// correctly and (if not) we are are notified on the initial thread (instead of on the network update thread)
|
|
||||||
KryoExtra<C> kryo = null;
|
|
||||||
try {
|
|
||||||
kryo = kryoPool.take();
|
|
||||||
|
|
||||||
ClassResolver classResolver = kryo.getClassResolver();
|
|
||||||
|
|
||||||
// now initialize the RMI cached methods, so that they are "final" when the network threads need access to it.
|
|
||||||
for (Object clazz : classesToRegister) {
|
|
||||||
if (clazz instanceof RemoteIfaceClass) {
|
|
||||||
// THIS IS DONE ON THE "CLIENT"
|
|
||||||
// "server" means the side of the connection that has the implementation details for the RMI object
|
|
||||||
// "client" means the side of the connection that accesses the "server" side object via a proxy object
|
|
||||||
// the client will NEVER send this object to the server.
|
|
||||||
// the server will ONLY send this object to the client, where on the client it becomes the proxy/interface.
|
|
||||||
RemoteIfaceClass remoteIfaceClass = (RemoteIfaceClass) clazz;
|
|
||||||
int id = classResolver.getRegistration(remoteIfaceClass.ifaceClass)
|
|
||||||
.getId();
|
|
||||||
|
|
||||||
CachedMethod[] cachedMethods = RmiUtils.getCachedMethods(Serialization.logger, kryo, useAsm,
|
|
||||||
remoteIfaceClass.ifaceClass,
|
|
||||||
null,
|
|
||||||
id);
|
|
||||||
methodCache.put(id, cachedMethods);
|
|
||||||
}
|
|
||||||
else if (clazz instanceof RemoteImplClass) {
|
|
||||||
// THIS IS DONE ON THE "SERVER"
|
|
||||||
// "server" means the side of the connection that has the implementation details for the RMI object
|
|
||||||
// "client" means the side of the connection that accesses the "server" side object via a proxy object
|
|
||||||
// the client will NEVER send this object to the server.
|
|
||||||
// the server will ONLY send this object to the client, where on the client it becomes the proxy/interface.
|
|
||||||
RemoteImplClass remoteImplClass = (RemoteImplClass) clazz;
|
|
||||||
int id = classResolver.getRegistration(remoteImplClass.implClass)
|
|
||||||
.getId();
|
|
||||||
|
|
||||||
CachedMethod[] cachedMethods = RmiUtils.getCachedMethods(Serialization.logger, kryo, useAsm,
|
|
||||||
remoteImplClass.ifaceClass,
|
|
||||||
remoteImplClass.implClass,
|
|
||||||
id);
|
|
||||||
methodCache.put(id, cachedMethods);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (kryo != null) {
|
|
||||||
kryoPool.put(kryo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
CachedMethod[] getMethods(final int classId) {
|
|
||||||
return methodCache.get(classId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized
|
|
||||||
boolean initialized() {
|
|
||||||
return initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until a kryo is available to write, using CAS operations to prevent having to synchronize.
|
* Waits until a kryo is available to write, using CAS operations to prevent having to synchronize.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -222,11 +222,8 @@ class RmiGlobalTest extends BaseTest {
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT();
|
||||||
register(configuration.serialization);
|
register(configuration.serialization);
|
||||||
|
|
||||||
// for Server -> Client RMI (ID: CLIENT_GLOBAL_OBJECT_ID) - NOTICE: none of the super classes/interfaces are registered!
|
// NOTICE: none of the super classes/interfaces are registered!
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
|
|
||||||
// for Client -> Server RMI (ID: SERVER_GLOBAL_OBJECT_ID) - NOTICE: none of the super classes/interfaces are registered!
|
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
|
||||||
|
|
||||||
final Server server = new Server(configuration);
|
final Server server = new Server(configuration);
|
||||||
server.setIdleTimeout(0);
|
server.setIdleTimeout(0);
|
||||||
|
@ -286,11 +283,8 @@ class RmiGlobalTest extends BaseTest {
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT();
|
||||||
register(configuration.serialization);
|
register(configuration.serialization);
|
||||||
|
|
||||||
// for Server -> Client RMI (ID: CLIENT_GLOBAL_OBJECT_ID)
|
// NOTICE: none of the super classes/interfaces are registered!
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
|
|
||||||
// for Client -> Server RMI (ID: SERVER_GLOBAL_OBJECT_ID)
|
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
|
||||||
|
|
||||||
|
|
||||||
final Client client = new Client(configuration);
|
final Client client = new Client(configuration);
|
||||||
|
@ -340,7 +334,8 @@ class RmiGlobalTest extends BaseTest {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.connect(5000);
|
// client.connect(5000);
|
||||||
|
client.connect(0);
|
||||||
waitForThreads();
|
waitForThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class RmiObjectIdExhaustionTest extends BaseTest {
|
||||||
manager.register(UnsupportedOperationException.class);
|
manager.register(UnsupportedOperationException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test // NOTE: change final to test!
|
// @Test // NOTE: remove final from RmiBridge.INVALID_RMI to test!
|
||||||
public
|
public
|
||||||
void rmiNetwork() throws SecurityException, IOException, InterruptedException {
|
void rmiNetwork() throws SecurityException, IOException, InterruptedException {
|
||||||
rmi(new Config() {
|
rmi(new Config() {
|
||||||
|
@ -60,7 +60,7 @@ class RmiObjectIdExhaustionTest extends BaseTest {
|
||||||
Thread.sleep(2000L);
|
Thread.sleep(2000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test // NOTE: change final to test!
|
// @Test // NOTE: remove final from RmiBridge.INVALID_RMI to test!
|
||||||
public
|
public
|
||||||
void rmiLocal() throws SecurityException, IOException, InterruptedException {
|
void rmiLocal() throws SecurityException, IOException, InterruptedException {
|
||||||
rmi(new Config() {
|
rmi(new Config() {
|
||||||
|
@ -80,7 +80,7 @@ class RmiObjectIdExhaustionTest extends BaseTest {
|
||||||
|
|
||||||
public
|
public
|
||||||
void rmi(final Config config) throws SecurityException, IOException {
|
void rmi(final Config config) throws SecurityException, IOException {
|
||||||
// NOTE: change final to test!
|
// NOTE: remove final from RmiBridge.INVALID_RMI to test!
|
||||||
// RmiBridge.INVALID_RMI = 4;
|
// RmiBridge.INVALID_RMI = 4;
|
||||||
|
|
||||||
Configuration configuration = new Configuration();
|
Configuration configuration = new Configuration();
|
||||||
|
@ -88,13 +88,7 @@ class RmiObjectIdExhaustionTest extends BaseTest {
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT();
|
||||||
register(configuration.serialization);
|
register(configuration.serialization);
|
||||||
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
// for Client -> Server RMI (ID 1)
|
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
|
||||||
|
|
||||||
// for Server -> Client RMI (ID 2)
|
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
|
||||||
|
|
||||||
|
|
||||||
final Server server = new Server(configuration);
|
final Server server = new Server(configuration);
|
||||||
server.setIdleTimeout(0);
|
server.setIdleTimeout(0);
|
||||||
|
@ -102,18 +96,16 @@ class RmiObjectIdExhaustionTest extends BaseTest {
|
||||||
addEndPoint(server);
|
addEndPoint(server);
|
||||||
server.bind(false);
|
server.bind(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
configuration = new Configuration();
|
configuration = new Configuration();
|
||||||
config.apply(configuration);
|
config.apply(configuration);
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT();
|
||||||
register(configuration.serialization);
|
register(configuration.serialization);
|
||||||
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
// for Client -> Server RMI (ID 1)
|
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
|
||||||
|
|
||||||
// for Server -> Client RMI (ID 2)
|
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
|
||||||
|
|
||||||
|
|
||||||
final Client client = new Client(configuration);
|
final Client client = new Client(configuration);
|
||||||
|
|
|
@ -106,9 +106,9 @@ class RmiSendObjectOverrideMethodTest extends BaseTest {
|
||||||
|
|
||||||
final boolean isUDP = configuration.udpPort > 0;
|
final boolean isUDP = configuration.udpPort > 0;
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
configuration.serialization.registerRmiImplementation(TestObject.class, TestObjectImpl.class);
|
configuration.serialization.registerRmi(TestObject.class, TestObjectImpl.class);
|
||||||
configuration.serialization.registerRmiImplementation(OtherObject.class, OtherObjectImpl.class);
|
configuration.serialization.registerRmi(OtherObject.class, OtherObjectImpl.class);
|
||||||
|
|
||||||
Server server = new Server(configuration);
|
Server server = new Server(configuration);
|
||||||
server.setIdleTimeout(0);
|
server.setIdleTimeout(0);
|
||||||
|
@ -138,9 +138,9 @@ class RmiSendObjectOverrideMethodTest extends BaseTest {
|
||||||
configuration = new Configuration();
|
configuration = new Configuration();
|
||||||
config.apply(configuration);
|
config.apply(configuration);
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
configuration.serialization.registerRmiInterface(TestObject.class);
|
configuration.serialization.registerRmi(TestObject.class, TestObjectImpl.class);
|
||||||
configuration.serialization.registerRmiInterface(OtherObject.class);
|
configuration.serialization.registerRmi(OtherObject.class, OtherObjectImpl.class);
|
||||||
|
|
||||||
Client client = new Client(configuration);
|
Client client = new Client(configuration);
|
||||||
client.setIdleTimeout(0);
|
client.setIdleTimeout(0);
|
||||||
|
|
|
@ -90,9 +90,9 @@ class RmiSendObjectTest extends BaseTest {
|
||||||
Configuration configuration = new Configuration();
|
Configuration configuration = new Configuration();
|
||||||
config.apply(configuration);
|
config.apply(configuration);
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
configuration.serialization.registerRmiImplementation(TestObject.class, TestObjectImpl.class);
|
configuration.serialization.registerRmi(TestObject.class, TestObjectImpl.class);
|
||||||
configuration.serialization.registerRmiImplementation(OtherObject.class, OtherObjectImpl.class);
|
configuration.serialization.registerRmi(OtherObject.class, OtherObjectImpl.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,9 +124,9 @@ class RmiSendObjectTest extends BaseTest {
|
||||||
configuration = new Configuration();
|
configuration = new Configuration();
|
||||||
config.apply(configuration);
|
config.apply(configuration);
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
configuration.serialization.registerRmiInterface(TestObject.class);
|
configuration.serialization.registerRmi(TestObject.class, TestObjectImpl.class);
|
||||||
configuration.serialization.registerRmiInterface(OtherObject.class);
|
configuration.serialization.registerRmi(OtherObject.class, OtherObjectImpl.class);
|
||||||
|
|
||||||
|
|
||||||
Client client = new Client(configuration);
|
Client client = new Client(configuration);
|
||||||
|
|
|
@ -171,6 +171,7 @@ class RmiTest extends BaseTest {
|
||||||
public static
|
public static
|
||||||
void register(dorkbox.network.serialization.CryptoSerializationManager manager) {
|
void register(dorkbox.network.serialization.CryptoSerializationManager manager) {
|
||||||
manager.register(Object.class); // Needed for Object#toString, hashCode, etc.
|
manager.register(Object.class); // Needed for Object#toString, hashCode, etc.
|
||||||
|
manager.register(TestCow.class);
|
||||||
manager.register(MessageWithTestCow.class);
|
manager.register(MessageWithTestCow.class);
|
||||||
manager.register(UnsupportedOperationException.class);
|
manager.register(UnsupportedOperationException.class);
|
||||||
}
|
}
|
||||||
|
@ -217,14 +218,14 @@ class RmiTest extends BaseTest {
|
||||||
Configuration configuration = new Configuration();
|
Configuration configuration = new Configuration();
|
||||||
config.apply(configuration);
|
config.apply(configuration);
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
register(configuration.serialization);
|
register(configuration.serialization);
|
||||||
|
|
||||||
// for Client -> Server RMI (ID 1)
|
// for Client -> Server RMI (ID 1)
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
|
|
||||||
// for Server -> Client RMI (ID 2)
|
// for Server -> Client RMI (ID 2)
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
// configuration.serialization.registerRmiInterface(TestCow.class);
|
||||||
|
|
||||||
|
|
||||||
final Server server = new Server(configuration);
|
final Server server = new Server(configuration);
|
||||||
|
@ -274,14 +275,15 @@ class RmiTest extends BaseTest {
|
||||||
configuration = new Configuration();
|
configuration = new Configuration();
|
||||||
config.apply(configuration);
|
config.apply(configuration);
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
register(configuration.serialization);
|
register(configuration.serialization);
|
||||||
|
|
||||||
// for Client -> Server RMI (ID 1)
|
// for Client -> Server RMI (ID 1)
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
// the 'TestCow' object lives on the 'Server'. The 'Client' accesses remote methods on it
|
||||||
|
// configuration.serialization.registerRmiInterface(TestCow.class);
|
||||||
|
|
||||||
// for Server -> Client RMI (ID 2)
|
// // for Server -> Client RMI (ID 2)
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
|
|
||||||
|
|
||||||
final Client client = new Client(configuration);
|
final Client client = new Client(configuration);
|
||||||
|
|
|
@ -37,8 +37,8 @@ class TestClient
|
||||||
|
|
||||||
// rootLogger.setLevel(Level.OFF);
|
// rootLogger.setLevel(Level.OFF);
|
||||||
|
|
||||||
rootLogger.setLevel(Level.DEBUG);
|
// rootLogger.setLevel(Level.DEBUG);
|
||||||
// rootLogger.setLevel(Level.TRACE);
|
rootLogger.setLevel(Level.TRACE);
|
||||||
// rootLogger.setLevel(Level.ALL);
|
// rootLogger.setLevel(Level.ALL);
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,14 +78,15 @@ class TestClient
|
||||||
configuration.udpPort = 2001;
|
configuration.udpPort = 2001;
|
||||||
configuration.host = "localhost";
|
configuration.host = "localhost";
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
RmiTest.register(configuration.serialization);
|
RmiTest.register(configuration.serialization);
|
||||||
configuration.serialization.registerRmiInterface(TestCow.class);
|
configuration.serialization.registerRmi(TestCow.class);
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Client client = new Client(configuration);
|
final Client client = new Client(configuration);
|
||||||
// client.setIdleTimeout(0);
|
client.disableRemoteKeyValidation();
|
||||||
|
client.setIdleTimeout(0);
|
||||||
|
|
||||||
client.listeners()
|
client.listeners()
|
||||||
.add(new dorkbox.network.connection.Listener.OnConnected<Connection>() {
|
.add(new dorkbox.network.connection.Listener.OnConnected<Connection>() {
|
||||||
|
@ -121,7 +122,7 @@ class TestClient
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
client.connect(3330);
|
client.connect(0);
|
||||||
|
|
||||||
client.waitForShutdown();
|
client.waitForShutdown();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -21,17 +21,18 @@ class TestServer
|
||||||
configuration.tcpPort = 2000;
|
configuration.tcpPort = 2000;
|
||||||
configuration.udpPort = 2001;
|
configuration.udpPort = 2001;
|
||||||
|
|
||||||
configuration.serialization = Serialization.DEFAULT();
|
configuration.serialization = Serialization.DEFAULT(true, true, false, null);
|
||||||
RmiTest.register(configuration.serialization);
|
RmiTest.register(configuration.serialization);
|
||||||
configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class);
|
configuration.serialization.registerRmi(TestCow.class, TestCowImpl.class);
|
||||||
|
|
||||||
Server server = null;
|
Server server = null;
|
||||||
try {
|
try {
|
||||||
server = new Server(configuration);
|
server = new Server(configuration);
|
||||||
|
server.disableRemoteKeyValidation();
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
// server.setIdleTimeout(0);
|
server.setIdleTimeout(0);
|
||||||
server.bind(true);
|
server.bind(true);
|
||||||
}}
|
}}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user