diff --git a/src/dorkbox/network/connection/CryptoSerializationManager.java b/src/dorkbox/network/connection/CryptoSerializationManager.java index 8d874450..d95d84e8 100644 --- a/src/dorkbox/network/connection/CryptoSerializationManager.java +++ b/src/dorkbox/network/connection/CryptoSerializationManager.java @@ -42,7 +42,7 @@ import com.esotericsoftware.kryo.util.IntMap; import com.esotericsoftware.kryo.util.MapReferenceResolver; import dorkbox.network.connection.ping.PingMessage; -import dorkbox.network.rmi.CachedMethod; +import dorkbox.network.rmi.ClassDefinitions; import dorkbox.network.rmi.InvocationHandlerSerializer; import dorkbox.network.rmi.InvocationResultSerializer; import dorkbox.network.rmi.InvokeMethod; @@ -153,9 +153,11 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa } private static class RemoteImplClass { + private final Class ifaceClass; private final Class implClass; - RemoteImplClass(final Class implClass) { + RemoteImplClass(final Class ifaceClass, final Class implClass) { + this.ifaceClass = ifaceClass; this.implClass = implClass; } } @@ -174,7 +176,9 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa private RemoteObjectSerializer remoteObjectSerializer; // used to track which interface -> implementation, for use by RMI - private final IntMap> rmiInterfaceToImpl = new IntMap>(); + private final IntMap> rmiIdToImpl = new IntMap>(); + private final IntMap> rmiIdToIface = new IntMap>(); + private final ClassDefinitions classDefinitions = new ClassDefinitions(); /** * By default, the serialization manager will compress+encrypt data to connections with remote IPs, and only compress on the loopback IP @@ -207,12 +211,12 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa public KryoExtra create() { synchronized (CryptoSerializationManager.this) { - KryoExtra kryo = new KryoExtra(); + KryoExtra kryo = new KryoExtra(CryptoSerializationManager.this); // we HAVE to pre-allocate the KRYOs boolean useAsm = !useUnsafeMemory; - kryo.setAsmEnabled(useAsm); + kryo.getFieldSerializerConfig().setUseAsm(useAsm); kryo.setRegistrationRequired(registrationRequired); kryo.setReferences(references); @@ -268,7 +272,8 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa int id = kryo.register(remoteImplClass.implClass, remoteObjectSerializer).getId(); // sets up the RMI, so when we receive the iface class from the client, we know what impl to use - rmiInterfaceToImpl.put(id, remoteImplClass.implClass); + rmiIdToImpl.put(id, remoteImplClass.implClass); + rmiIdToIface.put(id, remoteImplClass.ifaceClass); } } @@ -355,38 +360,12 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa } /** - * @return the previously registered class + * Enable remote method invocation (RMI) for this connection. 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 + * return value which is not {@link dorkbox.network.rmi.RemoteObject#setAsync(boolean) ignored}, 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. */ - private - Class getLastAddedClass() { - // get the previously registered class - Object obj = classesToRegister.get(classesToRegister.size() - 1); - if (obj instanceof Class) { - return (Class) obj; - } - else if (obj instanceof ClassSerializer) { - ClassSerializer classSerializer = (ClassSerializer) obj; - return classSerializer.clazz; - } - else if (obj instanceof ClassSerializer2) { - ClassSerializer2 classSerializer = (ClassSerializer2) obj; - return classSerializer.clazz; - } - - return null; - } - - private static - boolean testInterface(Class from, Class iface) { - for (Class intface : from.getInterfaces()) { - if (iface.equals(intface) || testInterface(intface, iface)) { - return true; - } - } - - return false; - } - @Override public synchronized RmiSerializationManager registerRmiInterface(Class ifaceClass) { @@ -421,13 +400,11 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa } usesRmi = true; + classesToRegister.add(new RemoteImplClass(ifaceClass, implClass)); - // THIS IS DONE ON THE SERVER ONLY - // we have to save the fact that we might have overridden methods. - // will throw IllegalArgumentException if the iface/impl have previously been overridden - CachedMethod.registerOverridden(ifaceClass, implClass); + // this MUST BE UNIQUE otherwise unexpected things can happen. + classDefinitions.set(ifaceClass, implClass); - classesToRegister.add(new RemoteImplClass(implClass)); return this; } @@ -472,6 +449,40 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa return initialized; } + /** + * Gets the RMI interface based on the specified ID (which is the ID for the registered implementation) + * + * @param objectId ID of the registered interface, which will map to the corresponding implementation. + * @return the implementation for the interface, or null + */ + @Override + public + Class getRmiIface(int objectId) { + return rmiIdToIface.get(objectId); + } + + /** + * Gets the RMI implementation based on the specified interface + * + * @return the corresponding implementation + */ + @Override + public + Class getRmiImpl(Class iface) { + return classDefinitions.get(iface); + } + + /** + * Gets the RMI interface based on the specified implementation + * + * @return the corresponding interface + */ + @Override + public + Class getRmiIface(Class implementation) { + return classDefinitions.getReverse(implementation); + } + /** * Gets the RMI implementation based on the specified ID (which is the ID for the registered interface) * @@ -479,8 +490,9 @@ class CryptoSerializationManager implements dorkbox.network.util.CryptoSerializa * @return the implementation for the interface, or null */ @Override - public Class getRmiImpl(int objectId) { - return rmiInterfaceToImpl.get(objectId); + public + Class getRmiImpl(int objectId) { + return rmiIdToImpl.get(objectId); } /** diff --git a/src/dorkbox/network/connection/KryoExtra.java b/src/dorkbox/network/connection/KryoExtra.java index 7d4a8a98..e79852a2 100644 --- a/src/dorkbox/network/connection/KryoExtra.java +++ b/src/dorkbox/network/connection/KryoExtra.java @@ -15,7 +15,14 @@ */ package dorkbox.network.connection; +import java.io.IOException; + +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.ParametersWithIV; + import com.esotericsoftware.kryo.Kryo; + import dorkbox.network.pipeline.ByteBufInput; import dorkbox.network.pipeline.ByteBufOutput; import dorkbox.util.bytes.BigEndian; @@ -26,11 +33,6 @@ import io.netty.buffer.Unpooled; import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4FastDecompressor; -import org.bouncycastle.crypto.engines.AESFastEngine; -import org.bouncycastle.crypto.modes.GCMBlockCipher; -import org.bouncycastle.crypto.params.ParametersWithIV; - -import java.io.IOException; /** * Nothing in this class is thread safe @@ -83,8 +85,11 @@ class KryoExtra extends Kryo { private byte[] decompressOutput; private ByteBuf decompressBuf; + private dorkbox.network.util.CryptoSerializationManager serializationManager; + public - KryoExtra() { + KryoExtra(final dorkbox.network.util.CryptoSerializationManager serializationManager) { + this.serializationManager = serializationManager; } public synchronized @@ -580,4 +585,9 @@ class KryoExtra extends Kryo { super.finalize(); } + + public + dorkbox.network.util.CryptoSerializationManager getSerializationManager() { + return serializationManager; + } } diff --git a/src/dorkbox/network/pipeline/LocalRmiDecoder.java b/src/dorkbox/network/pipeline/LocalRmiDecoder.java index 3cdb1a18..3fe2dcee 100644 --- a/src/dorkbox/network/pipeline/LocalRmiDecoder.java +++ b/src/dorkbox/network/pipeline/LocalRmiDecoder.java @@ -15,20 +15,18 @@ */ package dorkbox.network.pipeline; +import java.lang.reflect.Field; +import java.util.List; + import dorkbox.network.connection.ConnectionImpl; -import dorkbox.network.rmi.OverriddenMethods; import dorkbox.network.rmi.RemoteObject; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; -import java.lang.reflect.Field; -import java.util.List; - public class LocalRmiDecoder extends MessageToMessageDecoder { private static final RmiFieldCache fieldCache = RmiFieldCache.INSTANCE(); - private static final OverriddenMethods overriddenMethods = OverriddenMethods.INSTANCE(); public LocalRmiDecoder() { @@ -70,7 +68,10 @@ class LocalRmiDecoder extends MessageToMessageDecoder { throw new RuntimeException("Unable to get RMI interface object for RMI implementation"); } - final Class iface = overriddenMethods.getReverse(localRmiObject.getClass()); + + final Class iface = connection.getEndPoint() + .getSerialization() + .getRmiIface(localRmiObject.getClass()); if (iface == null) { throw new RuntimeException("Unable to get interface for RMI implementation"); } diff --git a/src/dorkbox/network/pipeline/LocalRmiEncoder.java b/src/dorkbox/network/pipeline/LocalRmiEncoder.java index fced8915..c006c0f8 100644 --- a/src/dorkbox/network/pipeline/LocalRmiEncoder.java +++ b/src/dorkbox/network/pipeline/LocalRmiEncoder.java @@ -15,20 +15,20 @@ */ package dorkbox.network.pipeline; -import dorkbox.network.connection.ConnectionImpl; -import dorkbox.network.connection.EndPoint; -import dorkbox.network.rmi.RMI; -import dorkbox.util.FastThreadLocal; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; - import java.lang.reflect.Field; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import dorkbox.network.connection.ConnectionImpl; +import dorkbox.network.connection.EndPoint; +import dorkbox.network.rmi.Rmi; +import dorkbox.util.FastThreadLocal; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; + @Sharable public class LocalRmiEncoder extends MessageToMessageEncoder { @@ -66,7 +66,7 @@ class LocalRmiEncoder extends MessageToMessageEncoder { Boolean needsTransform = transformObjectCache.get(implClass); if (needsTransform == null) { - boolean hasRmi = implClass.getAnnotation(RMI.class) != null; + boolean hasRmi = implClass.getAnnotation(Rmi.class) != null; if (hasRmi) { // replace object diff --git a/src/dorkbox/network/pipeline/RmiFieldCache.java b/src/dorkbox/network/pipeline/RmiFieldCache.java index a7ba8a7c..937cb33c 100644 --- a/src/dorkbox/network/pipeline/RmiFieldCache.java +++ b/src/dorkbox/network/pipeline/RmiFieldCache.java @@ -15,13 +15,14 @@ */ package dorkbox.network.pipeline; -import com.esotericsoftware.kryo.util.IdentityMap; -import dorkbox.network.rmi.RMI; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import com.esotericsoftware.kryo.util.IdentityMap; + +import dorkbox.network.rmi.Rmi; + /** * Uses the "single writer principle" for fast access, but disregards 'single writer', because duplicates are OK */ @@ -57,7 +58,7 @@ class RmiFieldCache { final ArrayList fields = new ArrayList(); for (Field field : clazz.getDeclaredFields()) { - if (field.getAnnotation(RMI.class) != null) { + if (field.getAnnotation(Rmi.class) != null) { fields.add(field); } } diff --git a/src/dorkbox/network/rmi/CachedMethod.java b/src/dorkbox/network/rmi/CachedMethod.java index d550d7fe..1cb864ca 100644 --- a/src/dorkbox/network/rmi/CachedMethod.java +++ b/src/dorkbox/network/rmi/CachedMethod.java @@ -55,6 +55,7 @@ import com.esotericsoftware.reflectasm.MethodAccess; import dorkbox.network.connection.Connection; import dorkbox.network.connection.EndPoint; import dorkbox.network.connection.KryoExtra; +import dorkbox.network.util.CryptoSerializationManager; import dorkbox.network.util.RmiSerializationManager; import dorkbox.util.ClassHelper; @@ -96,27 +97,34 @@ class CachedMethod { // the purpose of the method cache, is to accelerate looking up methods for specific class private static final Map, CachedMethod[]> methodCache = new ConcurrentHashMap, CachedMethod[]>(EndPoint.DEFAULT_THREAD_POOL_SIZE); - private static final OverriddenMethods overriddenMethods = OverriddenMethods.INSTANCE(); - // type will be likely be the interface - public static - CachedMethod[] getMethods(final Kryo kryo, final Class type) { + /** + * Called when we read a RMI method invocation on the "server" side (by kryo) + * + * @param type is the implementation type + */ + static + CachedMethod[] getMethods(final Kryo kryo, final Class type, final int classId) { CachedMethod[] cachedMethods = methodCache.get(type); if (cachedMethods != null) { return cachedMethods; } - cachedMethods = getCachedMethods(kryo, type); + cachedMethods = getCachedMethods(kryo, type, classId); methodCache.put(type, cachedMethods); return cachedMethods; } - // type will be likely be the interface - public static - CachedMethod[] getMethods(final RmiSerializationManager serializationManager, final Class type) { + /** + * Called when we write an RMI method invocation on the "client" side (by RmiProxyHandler) + * + * @param type this is the interface. + */ + static + CachedMethod[] getMethods(final RmiSerializationManager serializationManager, final Class type, final int classId) { CachedMethod[] cachedMethods = methodCache.get(type); if (cachedMethods != null) { return cachedMethods; @@ -124,7 +132,7 @@ class CachedMethod { final KryoExtra kryo = serializationManager.takeKryo(); try { - cachedMethods = getCachedMethods(kryo, type); + cachedMethods = getCachedMethods(kryo, type, classId); methodCache.put(type, cachedMethods); } finally { serializationManager.returnKryo(kryo); @@ -133,10 +141,25 @@ class CachedMethod { return cachedMethods; } + // race-conditions are OK, because we just recreate the same thing. private static - CachedMethod[] getCachedMethods(final Kryo kryo, final Class type) { - // race-conditions are OK, because we just recreate the same thing. - final ArrayList methods = getMethods(type); + CachedMethod[] getCachedMethods(final Kryo kryo, final Class type, final int classId) { + // sometimes, the method index is based upon an interface and NOT the implementation. We have to clear that up here. + CryptoSerializationManager serialization = ((KryoExtra) kryo).getSerializationManager(); + + // when there is an interface available, we want to use that instead of the implementation. This is because the incoming + // implementation is ACTUALLY mapped (on the "client" side) to the interface. If we don't use the interface, we will have the + // wrong order of methods, so invoking a method by it's index will fail. + Class interfaceClass = serialization.getRmiIface(classId); + + final ArrayList methods; + + if (interfaceClass == null) { + methods = getMethods(type); + } else { + methods = getMethods(interfaceClass); + } + final int size = methods.size(); final CachedMethod[] cachedMethods = new CachedMethod[size]; @@ -155,8 +178,17 @@ class CachedMethod { // To facilitate this functionality, for methods with the same name, the "overriding" method is the one that inherits the Connection // interface as the first parameter, and .registerRemote(ifaceClass, implClass) must be called. - final IdentityMap overriddenMethods = getOverriddenMethods(type, methods); - final boolean asmEnabled = kryo.getAsmEnabled(); + + // will only be valid if implementation type is NOT NULL (otherwise will be null) + final IdentityMap overriddenMethods; + if (interfaceClass == null) { + overriddenMethods = null; + } else { + // type here must be the implementation + overriddenMethods = getOverriddenMethods(type, methods); + } + + final boolean asmEnabled = kryo.getFieldSerializerConfig().isUseAsm(); MethodAccess methodAccess = null; // reflectASM can't get any method from the 'Object' object, and it MUST be public @@ -173,6 +205,7 @@ class CachedMethod { for (int i = 0; i < size; i++) { final Method origMethod = methods.get(i); Method method = origMethod; // copy because one or more can be overridden + Class declaringClass = method.getDeclaringClass(); MethodAccess localMethodAccess = methodAccess; // copy because one or more can be overridden Class[] parameterTypes = method.getParameterTypes(); Class[] asmParameterTypes = parameterTypes; @@ -184,7 +217,7 @@ class CachedMethod { // the correct object for this overridden method to be called on. method = overriddenMethod; - Class overrideType = method.getDeclaringClass(); + Class overrideType = declaringClass; if (asmEnabled && !Util.isAndroid && Modifier.isPublic(overrideType.getModifiers())) { localMethodAccess = MethodAccess.get(overrideType); asmParameterTypes = method.getParameterTypes(); @@ -203,7 +236,7 @@ class CachedMethod { cachedMethod = asmCachedMethod; } catch (Exception e) { if (logger.isTraceEnabled()) { - logger.trace("Unable to use ReflectAsm for {}.{}", method.getDeclaringClass(), method.getName(), e); + logger.trace("Unable to use ReflectAsm for {}.{}", declaringClass, method.getName(), e); } } } @@ -213,7 +246,18 @@ class CachedMethod { } cachedMethod.method = method; cachedMethod.origMethod = origMethod; - cachedMethod.methodClassID = kryo.getRegistration(method.getDeclaringClass()).getId(); + + + // on the "server", we only register the implementation. NOT THE INTERFACE, so for RMI classes, we have to get the impl + Class impl = serialization.getRmiImpl(declaringClass); + if (impl != null) { + cachedMethod.methodClassID = kryo.getRegistration(impl) + .getId(); + } + else { + cachedMethod.methodClassID = kryo.getRegistration(declaringClass) + .getId(); + } cachedMethod.methodIndex = i; // Store the serializer for each final parameter. @@ -231,54 +275,48 @@ class CachedMethod { return cachedMethods; } + // does not null check private static IdentityMap getOverriddenMethods(final Class type, final ArrayList origMethods) { - final Class implType = overriddenMethods.get(type); + final ArrayList implMethods = getMethods(type); + final IdentityMap overrideMap = new IdentityMap(implMethods.size()); - if (implType != null) { - final ArrayList implMethods = getMethods(implType); - final IdentityMap overrideMap = new IdentityMap(implMethods.size()); + for (Method origMethod : origMethods) { + String name = origMethod.getName(); + Class[] origTypes = origMethod.getParameterTypes(); + int origLength = origTypes.length + 1; - for (Method origMethod : origMethods) { - String name = origMethod.getName(); - Class[] origTypes = origMethod.getParameterTypes(); - int origLength = origTypes.length + 1; + METHOD_CHECK: + for (Method implMethod : implMethods) { + String checkName = implMethod.getName(); + Class[] checkTypes = implMethod.getParameterTypes(); + int checkLength = checkTypes.length; - METHOD_CHECK: - for (Method implMethod : implMethods) { - String checkName = implMethod.getName(); - Class[] checkTypes = implMethod.getParameterTypes(); - int checkLength = checkTypes.length; + if (origLength != checkLength || !(name.equals(checkName))) { + continue; + } - if (origLength != checkLength || !(name.equals(checkName))) { - continue; + // checkLength > 0 + Class shouldBeConnectionType = checkTypes[0]; + if (ClassHelper.hasInterface(dorkbox.network.connection.Connection.class, shouldBeConnectionType)) { + // now we check to see if our "check" method is equal to our "cached" method + Connection + if (checkLength == 1) { + overrideMap.put(origMethod, implMethod); + break; } - - // checkLength > 0 - Class shouldBeConnectionType = checkTypes[0]; - if (ClassHelper.hasInterface(dorkbox.network.connection.Connection.class, shouldBeConnectionType)) { - // now we check to see if our "check" method is equal to our "cached" method + Connection - if (checkLength == 1) { - overrideMap.put(origMethod, implMethod); - break; - } - else { - for (int k = 1; k < checkLength; k++) { - if (origTypes[k - 1] == checkTypes[k]) { - overrideMap.put(origMethod, implMethod); - break METHOD_CHECK; - } + else { + for (int k = 1; k < checkLength; k++) { + if (origTypes[k - 1] == checkTypes[k]) { + overrideMap.put(origMethod, implMethod); + break METHOD_CHECK; } } } } } + } - return overrideMap; - } - else { - return null; - } + return overrideMap; } private static @@ -314,18 +352,6 @@ class CachedMethod { return methods; } - /** - * Called by the SerializationManager, so that RMI classes that are overridden for serialization purposes, can check to see if certain - * methods need to be overridden. - *

- * NOTE: It is CRITICAL that this is unique per JVM, otherwise unexpected things can happen. - * - * @throws IllegalArgumentException if the iface/impl have previously been overridden - */ - public static - void registerOverridden(final Class ifaceClass, final Class implClass) { - overriddenMethods.set(ifaceClass, implClass); - } public Method method; public int methodClassID; diff --git a/src/dorkbox/network/rmi/OverriddenMethods.java b/src/dorkbox/network/rmi/ClassDefinitions.java similarity index 80% rename from src/dorkbox/network/rmi/OverriddenMethods.java rename to src/dorkbox/network/rmi/ClassDefinitions.java index 63b866a8..f3e44c29 100644 --- a/src/dorkbox/network/rmi/OverriddenMethods.java +++ b/src/dorkbox/network/rmi/ClassDefinitions.java @@ -23,28 +23,23 @@ import com.esotericsoftware.kryo.util.IdentityMap; * Uses the "single writer principle" for fast access */ public -class OverriddenMethods { +class ClassDefinitions { // not concurrent because they are setup during system initialization private volatile IdentityMap, Class> overriddenMethods = new IdentityMap, Class>(); private volatile IdentityMap, Class> overriddenReverseMethods = new IdentityMap, Class>(); - private static final AtomicReferenceFieldUpdater overriddenMethodsREF = - AtomicReferenceFieldUpdater.newUpdater(OverriddenMethods.class, + private static final AtomicReferenceFieldUpdater overriddenMethodsREF = + AtomicReferenceFieldUpdater.newUpdater(ClassDefinitions.class, IdentityMap.class, "overriddenMethods"); - private static final AtomicReferenceFieldUpdater overriddenReverseMethodsREF = - AtomicReferenceFieldUpdater.newUpdater(OverriddenMethods.class, + private static final AtomicReferenceFieldUpdater overriddenReverseMethodsREF = + AtomicReferenceFieldUpdater.newUpdater(ClassDefinitions.class, IdentityMap.class, "overriddenReverseMethods"); - private static final OverriddenMethods INSTANCE = new OverriddenMethods(); - public static synchronized OverriddenMethods INSTANCE() { - return INSTANCE; - } - - private - OverriddenMethods() { + public + ClassDefinitions() { } /** @@ -76,7 +71,7 @@ class OverriddenMethods { Class a = this.overriddenMethods.put(ifaceClass, implClass); Class b = this.overriddenReverseMethods.put(implClass, ifaceClass); - // this MUST BE UNIQUE per JVM, otherwise unexpected things can happen. + // this MUST BE UNIQUE otherwise unexpected things can happen. if (a != null || b != null) { 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" + diff --git a/src/dorkbox/network/rmi/InvokeMethodSerializer.java b/src/dorkbox/network/rmi/InvokeMethodSerializer.java index 914f3af5..afcce2ff 100644 --- a/src/dorkbox/network/rmi/InvokeMethodSerializer.java +++ b/src/dorkbox/network/rmi/InvokeMethodSerializer.java @@ -54,16 +54,22 @@ class InvokeMethodSerializer extends Serializer { @SuppressWarnings("rawtypes") public void write(final Kryo kryo, final Output output, final InvokeMethod object) { + // 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.cachedMethod.methodClassID, true); output.writeByte(object.cachedMethod.methodIndex); Serializer[] serializers = object.cachedMethod.serializers; + int length = serializers.length; + Object[] args = object.args; - int i = 0, n = serializers.length; - for (; i < n; i++) { + for (int i = 0; i < length; i++) { Serializer serializer = serializers[i]; + if (serializer != null) { kryo.writeObjectOrNull(output, args[i], serializer); } @@ -84,20 +90,22 @@ class InvokeMethodSerializer extends Serializer { int methodClassID = input.readInt(true); Class methodClass = kryo.getRegistration(methodClassID) - .getType(); + .getType(); byte methodIndex = input.readByte(); + CachedMethod cachedMethod; try { - invokeMethod.cachedMethod = CachedMethod.getMethods(kryo, methodClass)[methodIndex]; + cachedMethod = CachedMethod.getMethods(kryo, methodClass, methodClassID)[methodIndex]; + invokeMethod.cachedMethod = cachedMethod; } catch (IndexOutOfBoundsException ex) { throw new KryoException("Invalid method index " + methodIndex + " for class: " + methodClass.getName()); } - CachedMethod cachedMethod = invokeMethod.cachedMethod; Serializer[] serializers = cachedMethod.serializers; Class[] parameterTypes = cachedMethod.method.getParameterTypes(); Object[] args = new Object[serializers.length]; invokeMethod.args = args; + for (int i = 0, n = args.length; i < n; i++) { Serializer serializer = serializers[i]; if (serializer != null) { diff --git a/src/dorkbox/network/rmi/RemoteObjectSerializer.java b/src/dorkbox/network/rmi/RemoteObjectSerializer.java index 14e118db..1c7c68ca 100644 --- a/src/dorkbox/network/rmi/RemoteObjectSerializer.java +++ b/src/dorkbox/network/rmi/RemoteObjectSerializer.java @@ -42,7 +42,7 @@ import com.esotericsoftware.kryo.io.Output; import dorkbox.network.connection.KryoExtra; /** - * Serializes an object registered with the RmiImplHandler so the receiving side gets a {@link RemoteObject} proxy rather than the bytes for the + * Serializes an object registered with the RmiBridge so the receiving side gets a {@link RemoteObject} proxy rather than the bytes for the * serialized object. * * @author Nathan Sweet diff --git a/src/dorkbox/network/rmi/RMI.java b/src/dorkbox/network/rmi/Rmi.java similarity index 86% rename from src/dorkbox/network/rmi/RMI.java rename to src/dorkbox/network/rmi/Rmi.java index bbdba0d6..3737e660 100644 --- a/src/dorkbox/network/rmi/RMI.java +++ b/src/dorkbox/network/rmi/Rmi.java @@ -15,7 +15,11 @@ */ package dorkbox.network.rmi; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * This specifies to the serializer, that this class contains an RMI object, and that a specific field is an RMI object. Both are @@ -32,4 +36,4 @@ import java.lang.annotation.*; @Inherited @Target(value = {ElementType.TYPE, ElementType.FIELD}) public -@interface RMI {} +@interface Rmi {} diff --git a/src/dorkbox/network/rmi/RmiImplHandler.java b/src/dorkbox/network/rmi/RmiBridge.java similarity index 92% rename from src/dorkbox/network/rmi/RmiImplHandler.java rename to src/dorkbox/network/rmi/RmiBridge.java index 218a8514..a77b8cf7 100644 --- a/src/dorkbox/network/rmi/RmiImplHandler.java +++ b/src/dorkbox/network/rmi/RmiBridge.java @@ -87,7 +87,7 @@ import dorkbox.util.collections.ObjectIntMap; * @author Nathan Sweet , Nathan Robinson */ public final -class RmiImplHandler { +class RmiBridge { public static final int INVALID_RMI = 0; static final int returnValueMask = 1 << 7; static final int returnExceptionMask = 1 << 6; @@ -105,7 +105,7 @@ class RmiImplHandler { return (objectId & 1) != 0; } - // the name of who created this RmiImplHandler + // the name of who created this RmiBridge private final org.slf4j.Logger logger; // we start at 1, because 0 (INVALID_RMI) means we access connection only objects @@ -130,7 +130,7 @@ class RmiImplHandler { final Object target = connection.getImplementationObject(objectID); if (target == null) { - Logger logger2 = RmiImplHandler.this.logger; + Logger logger2 = RmiBridge.this.logger; if (logger2.isWarnEnabled()) { logger2.warn("Ignoring remote invocation request for unknown object ID: {}", objectID); } @@ -138,7 +138,7 @@ class RmiImplHandler { return; } - Executor executor2 = RmiImplHandler.this.executor; + Executor executor2 = RmiBridge.this.executor; if (executor2 == null) { try { invoke(connection, target, invokeMethod); @@ -163,18 +163,18 @@ class RmiImplHandler { }; /** - * Creates an RmiImplHandler with no connections. Connections must be {@link RmiImplHandler#register(int, Object)} added to allow the remote end + * Creates an RmiBridge with no connections. Connections must be {@link RmiBridge#register(int, Object)} added to allow the remote end * of the connections to access objects in this ObjectSpace. * * @param executor * Sets the executor used to invoke methods when an invocation is received from a remote endpoint. By default, no * executor is set and invocations occur on the network thread, which should not be blocked for long, May be null. * @param isGlobal - * specify if this RmiImplHandler is a "global" bridge, meaning connections will prefer objects from this bridge instead of + * specify if this RmiBridge is a "global" bridge, meaning connections will prefer objects from this bridge instead of * the connection-local bridge. */ public - RmiImplHandler(final org.slf4j.Logger logger, final Executor executor, final boolean isGlobal) { + RmiBridge(final org.slf4j.Logger logger, final Executor executor, final boolean isGlobal) { this.logger = logger; this.executor = executor; @@ -197,7 +197,7 @@ class RmiImplHandler { /** * Invokes the method on the object and, if necessary, sends the result back to the connection that made the invocation request. This - * method is invoked on the update thread of the {@link EndPoint} for this RmiImplHandler and unless an executor has been set. + * method is invoked on the update thread of the {@link EndPoint} for this RmiBridge and unless an executor has been set. * * @param connection * The remote side of this connection requested the invocation. @@ -298,13 +298,13 @@ class RmiImplHandler { int value = rmiObjectIdCounter.getAndAdd(2); if (value > MAX_RMI_VALUE) { rmiObjectIdCounter.set(MAX_RMI_VALUE); // prevent wrapping by spammy callers - logger.error("RMI next value has exceeded maximum limits in RmiImplHandler!"); + logger.error("RMI next value has exceeded maximum limits in RmiBridge!"); } return value; } /** - * Registers an object to allow the remote end of the RmiImplHandler connections to access it using the specified ID. + * Registers an object to allow the remote end of the RmiBridge connections to access it using the specified ID. * * @param objectID * Must not be Integer.MAX_VALUE. @@ -319,7 +319,7 @@ class RmiImplHandler { throw new IllegalArgumentException("object cannot be null."); } - WriteLock writeLock = RmiImplHandler.this.objectLock.writeLock(); + WriteLock writeLock = RmiBridge.this.objectLock.writeLock(); writeLock.lock(); this.idToObject.put(objectID, object); @@ -334,12 +334,12 @@ class RmiImplHandler { } /** - * Removes an object. The remote end of the RmiImplHandler connection will no longer be able to access it. + * Removes an object. The remote end of the RmiBridge connection will no longer be able to access it. */ @SuppressWarnings("AutoBoxing") public void remove(int objectID) { - WriteLock writeLock = RmiImplHandler.this.objectLock.writeLock(); + WriteLock writeLock = RmiBridge.this.objectLock.writeLock(); writeLock.lock(); Object object = this.idToObject.remove(objectID); @@ -356,12 +356,12 @@ class RmiImplHandler { } /** - * Removes an object. The remote end of the RmiImplHandler connection will no longer be able to access it. + * Removes an object. The remote end of the RmiBridge connection will no longer be able to access it. */ @SuppressWarnings("AutoBoxing") public void remove(Object object) { - WriteLock writeLock = RmiImplHandler.this.objectLock.writeLock(); + WriteLock writeLock = RmiBridge.this.objectLock.writeLock(); writeLock.lock(); if (!this.idToObject.containsValue(object, true)) { @@ -417,7 +417,7 @@ class RmiImplHandler { *

* Returns a proxy object that implements the specified interfaces. Methods invoked on the proxy object will be invoked remotely on the * object with the specified ID in the ObjectSpace for the specified connection. If the remote end of the connection has not {@link - * RmiImplHandler#register(int, Object)} added the connection to the ObjectSpace, the remote method invocations will be ignored. + * RmiBridge#register(int, Object)} added the connection to the ObjectSpace, the remote method invocations will be ignored. *

* Methods that return a value will throw {@link TimeoutException} if the response is not received with the {@link * RemoteObject#setResponseTimeout(int) response timeout}. @@ -444,7 +444,7 @@ class RmiImplHandler { temp[0] = RemoteObject.class; temp[1] = iface; - return (RemoteObject) Proxy.newProxyInstance(RmiImplHandler.class.getClassLoader(), + return (RemoteObject) Proxy.newProxyInstance(RmiBridge.class.getClassLoader(), temp, new RmiProxyHandler(connection, objectID)); } diff --git a/src/dorkbox/network/rmi/RmiProxyHandler.java b/src/dorkbox/network/rmi/RmiProxyHandler.java index 8b6a42e4..5515f75c 100644 --- a/src/dorkbox/network/rmi/RmiProxyHandler.java +++ b/src/dorkbox/network/rmi/RmiProxyHandler.java @@ -207,7 +207,7 @@ class RmiProxyHandler implements InvocationHandler { } EndPoint endPoint = this.connection.getEndPoint(); - final RmiSerializationManager serializationManager = (RmiSerializationManager) endPoint.getSerialization(); + final RmiSerializationManager serializationManager = endPoint.getSerialization(); InvokeMethod invokeMethod = new InvokeMethod(); invokeMethod.objectID = this.objectID; @@ -215,7 +215,7 @@ class RmiProxyHandler implements InvocationHandler { // which method do we access? - CachedMethod[] cachedMethods = CachedMethod.getMethods(serializationManager, method.getDeclaringClass()); + CachedMethod[] cachedMethods = CachedMethod.getMethods(serializationManager, method.getDeclaringClass(), invokeMethod.objectID); for (int i = 0, n = cachedMethods.length; i < n; i++) { CachedMethod cachedMethod = cachedMethods[i]; @@ -255,10 +255,9 @@ class RmiProxyHandler implements InvocationHandler { byte responseID = (byte) 0; // An invocation doesn't need a response is if it's - // VOID return type // ASYNC and no return values or exceptions are wanted back Class returnType = method.getReturnType(); - boolean ignoreResponse = returnType == void.class || this.isAsync && !(this.transmitReturnValue || this.transmitExceptions); + boolean ignoreResponse = this.isAsync && !(this.transmitReturnValue || this.transmitExceptions); if (ignoreResponse) { invokeMethod.responseData = (byte) 0; // 0 means do not respond. } @@ -266,7 +265,7 @@ class RmiProxyHandler implements InvocationHandler { synchronized (this) { // Increment the response counter and put it into the low bits of the responseID. responseID = this.nextResponseId++; - if (this.nextResponseId > RmiImplHandler.responseIdMask) { + if (this.nextResponseId > RmiBridge.responseIdMask) { this.nextResponseId = (byte) 1; } this.pendingResponses[responseID] = true; @@ -274,15 +273,17 @@ class RmiProxyHandler implements InvocationHandler { // Pack other data into the high bits. byte responseData = responseID; if (this.transmitReturnValue) { - responseData |= (byte) RmiImplHandler.returnValueMask; + responseData |= (byte) RmiBridge.returnValueMask; } if (this.transmitExceptions) { - responseData |= (byte) RmiImplHandler.returnExceptionMask; + responseData |= (byte) RmiBridge.returnExceptionMask; } invokeMethod.responseData = responseData; } - // Sends our invokeMethod to the remote connection, which the RmiImplHandler listens for + this.lastResponseID = (byte) (invokeMethod.responseData & RmiBridge.responseIdMask); + + // Sends our invokeMethod to the remote connection, which the RmiBridge listens for if (this.udp) { this.connection.send() .UDP(invokeMethod) @@ -310,10 +311,7 @@ class RmiProxyHandler implements InvocationHandler { "#" + method.getName() + "(" + argString + ")"); } - this.lastResponseID = (byte) (invokeMethod.responseData & RmiImplHandler.responseIdMask); - // 0 means respond immediately because it's - // VOID return type // ASYNC and no return values or exceptions are wanted back if (this.isAsync) { if (returnType.isPrimitive()) { @@ -341,15 +339,9 @@ class RmiProxyHandler implements InvocationHandler { if (returnType == double.class) { return 0.0d; } - if (returnType == void.class) { - return 0.0d; - } } return null; } - else if (returnType == void.class) { - return null; - } try { Object result = waitForResponse(this.lastResponseID); @@ -375,11 +367,21 @@ class RmiProxyHandler implements InvocationHandler { */ private Object waitForResponse(final byte responseID) throws IOException { - long endTime = System.currentTimeMillis() + this.timeoutMillis; - long remaining = this.timeoutMillis; + // if timeout == 0, we wait "forever" + long remaining; + long endTime; - if (remaining == 0) { - // just wait however log it takes. + if (this.timeoutMillis != 0) { + remaining = this.timeoutMillis; + endTime = System.currentTimeMillis() + remaining; + } else { + // not forever, but close enough + remaining = Long.MAX_VALUE; + endTime = Long.MAX_VALUE; + } + + // wait for the specified time + while (remaining > 0) { InvokeMethodResult invokeMethodResult; synchronized (this) { invokeMethodResult = this.responseTable[responseID]; @@ -392,7 +394,7 @@ class RmiProxyHandler implements InvocationHandler { else { this.lock.lock(); try { - this.responseCondition.await(); + this.responseCondition.await(remaining, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread() .interrupt(); @@ -402,41 +404,7 @@ class RmiProxyHandler implements InvocationHandler { } } - synchronized (this) { - invokeMethodResult = this.responseTable[responseID]; - } - if (invokeMethodResult != null) { - this.lastResponseID = null; - return invokeMethodResult.result; - } - } - else { - // wait for the specified time - while (remaining > 0) { - InvokeMethodResult invokeMethodResult; - synchronized (this) { - invokeMethodResult = this.responseTable[responseID]; - } - - if (invokeMethodResult != null) { - this.lastResponseID = null; - return invokeMethodResult.result; - } - else { - this.lock.lock(); - try { - this.responseCondition.await(remaining, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread() - .interrupt(); - throw new IOException("Response timed out.", e); - } finally { - this.lock.unlock(); - } - } - - remaining = endTime - System.currentTimeMillis(); - } + remaining = endTime - System.currentTimeMillis(); } // only get here if we timeout diff --git a/src/dorkbox/network/util/RmiSerializationManager.java b/src/dorkbox/network/util/RmiSerializationManager.java index c33adb4c..4558cd5d 100644 --- a/src/dorkbox/network/util/RmiSerializationManager.java +++ b/src/dorkbox/network/util/RmiSerializationManager.java @@ -68,6 +68,7 @@ interface RmiSerializationManager extends SerializationManager { /** * Necessary to register classes for RMI, only called once when the RMI bridge is created. + * * @return true if there are classes that have been registered for RMI */ boolean initRmiSerialization(); @@ -82,6 +83,15 @@ interface RmiSerializationManager extends SerializationManager { */ void returnKryo(KryoExtra kryo); + /** + * Gets the RMI interface based on the specified ID (which is the ID for the registered implementation) + * + * @param objectId ID of the registered interface, which will map to the corresponding implementation. + * + * @return the implementation for the interface, or null + */ + Class getRmiIface(int objectId); + /** * Gets the RMI implementation based on the specified ID (which is the ID for the registered interface) * @@ -91,6 +101,20 @@ interface RmiSerializationManager extends SerializationManager { */ Class getRmiImpl(int objectId); + /** + * Gets the RMI implementation based on the specified interface + * + * @return the corresponding implementation + */ + Class getRmiImpl(Class iface); + + /** + * Gets the RMI interface based on the specified implementation + * + * @return the corresponding interface + */ + Class getRmiIface(Class implementation); + /** * Enable remote method invocation (RMI) for this connection. There is additional overhead to using RMI. *

@@ -100,8 +124,6 @@ interface RmiSerializationManager extends SerializationManager { */ RmiSerializationManager registerRmiInterface(Class ifaceClass); - - /** * Objects that we want to use RMI with, must be accessed via an interface. This method configures the serialization of an * implementation to be serialized via the defined interface, as a RemoteObject (ie: proxy object). If the implementation class is diff --git a/test/dorkbox/network/rmi/MessageWithTestObject.java b/test/dorkbox/network/rmi/MessageWithTestCow.java similarity index 52% rename from test/dorkbox/network/rmi/MessageWithTestObject.java rename to test/dorkbox/network/rmi/MessageWithTestCow.java index f96a3743..7167a0d0 100644 --- a/test/dorkbox/network/rmi/MessageWithTestObject.java +++ b/test/dorkbox/network/rmi/MessageWithTestCow.java @@ -4,8 +4,8 @@ package dorkbox.network.rmi; * */ public -class MessageWithTestObject implements RmiMessages { +class MessageWithTestCow implements RmiMessages { public int number; public String text; - public TestObject testObject; + public TestCow testCow; } diff --git a/test/dorkbox/network/rmi/RmiGlobalTest.java b/test/dorkbox/network/rmi/RmiGlobalTest.java index 749667a3..9bdaa748 100644 --- a/test/dorkbox/network/rmi/RmiGlobalTest.java +++ b/test/dorkbox/network/rmi/RmiGlobalTest.java @@ -40,7 +40,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; -import java.io.Serializable; import org.junit.Test; @@ -61,11 +60,11 @@ class RmiGlobalTest extends BaseTest { private int CLIENT_GLOBAL_OBJECT_ID = 0; private int SERVER_GLOBAL_OBJECT_ID = 0; - private final TestObject globalRemoteServerObject = new TestObjectImpl(); - private final TestObject globalRemoteClientObject = new TestObjectImpl(); + private final TestCow globalRemoteServerObject = new TestCowImpl(); + private final TestCow globalRemoteClientObject = new TestCowImpl(); private static - void runTest(final Connection connection, final TestObject rObject, final TestObject test, final int remoteObjectID) { + void runTest(final Connection connection, final TestCow rObject, final TestCow test, final int remoteObjectID) { System.err.println("Starting test for: " + remoteObjectID); assertEquals(rObject.hashCode(), test.hashCode()); @@ -170,10 +169,10 @@ class RmiGlobalTest extends BaseTest { // Test sending a reference to a remote object. - MessageWithTestObject m = new MessageWithTestObject(); + MessageWithTestCow m = new MessageWithTestCow(); m.number = 678; m.text = "sometext"; - m.testObject = test; + m.testCow = test; connection.send() .TCP(m) .flush(); @@ -184,10 +183,7 @@ class RmiGlobalTest extends BaseTest { public static void register(dorkbox.network.util.CryptoSerializationManager manager) { manager.register(Object.class); // Needed for Object#toString, hashCode, etc. - - // manager.rmi().register(TestObject.class).override(TestObject.class, TestObjectImpl.class); - manager.register(MessageWithTestObject.class); - + manager.register(MessageWithTestCow.class); manager.register(UnsupportedOperationException.class); } @@ -202,6 +198,12 @@ class RmiGlobalTest extends BaseTest { configuration.serialization = CryptoSerializationManager.DEFAULT(); register(configuration.serialization); + // for Server -> Client RMI (ID: CLIENT_GLOBAL_OBJECT_ID) + configuration.serialization.registerRmiInterface(TestCow.class); + + // for Client -> Server RMI (ID: SERVER_GLOBAL_OBJECT_ID) + configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class); + final Server server = new Server(configuration); server.setIdleTimeout(0); @@ -218,11 +220,20 @@ class RmiGlobalTest extends BaseTest { public void connected(final Connection connection) { try { - connection.getRemoteObject(CLIENT_GLOBAL_OBJECT_ID, new RemoteObjectCallback() { + connection.getRemoteObject(CLIENT_GLOBAL_OBJECT_ID, new RemoteObjectCallback() { @Override public - void created(final TestObject remoteObject) { - runTest(connection, globalRemoteClientObject, remoteObject, CLIENT_GLOBAL_OBJECT_ID); + void created(final TestCow remoteObject) { + // MUST run on a separate thread because remote object method invocations are blocking + new Thread() { + @Override + public + void run() { + System.err.println("Running test for: Server -> Client"); + runTest(connection, globalRemoteClientObject, remoteObject, CLIENT_GLOBAL_OBJECT_ID); + System.err.println("Done with test for: Server -> Client"); + } + }.start(); } }); } catch (IOException e) { @@ -233,14 +244,17 @@ class RmiGlobalTest extends BaseTest { }); server.listeners() - .add(new Listener.OnMessageReceived() { + .add(new Listener.OnMessageReceived() { @Override public - void received(Connection connection, MessageWithTestObject m) { - TestObject object = m.testObject; + void received(Connection connection, MessageWithTestCow m) { + System.err.println("Received finish signal for test for: Client -> Server"); + + TestCow object = m.testCow; final int id = object.id(); - assertEquals(1, id); - System.err.println("Client/Server Finished!"); + assertEquals(SERVER_GLOBAL_OBJECT_ID, id); + + System.err.println("Finished test for: Client -> Server"); stopEndPoints(2000); } @@ -248,32 +262,59 @@ class RmiGlobalTest extends BaseTest { // ---- + configuration = new Configuration(); + configuration.tcpPort = tcpPort; + configuration.udpPort = udpPort; + configuration.host = host; + + configuration.serialization = CryptoSerializationManager.DEFAULT(); + register(configuration.serialization); + + // for Server -> Client RMI (ID: CLIENT_GLOBAL_OBJECT_ID) + configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class); + + // for Client -> Server RMI (ID: SERVER_GLOBAL_OBJECT_ID) + configuration.serialization.registerRmiInterface(TestCow.class); + final Client client = new Client(configuration); client.setIdleTimeout(0); + // register this object as a global object that the server will get CLIENT_GLOBAL_OBJECT_ID = client.createGlobalObject(globalRemoteClientObject); addEndPoint(client); client.listeners() - .add(new Listener.OnMessageReceived() { + .add(new Listener.OnMessageReceived() { @Override public - void received(Connection connection, MessageWithTestObject m) { - TestObject object = m.testObject; + void received(Connection connection, MessageWithTestCow m) { + System.err.println("Received finish signal for test for: Server -> Client"); + + TestCow object = m.testCow; final int id = object.id(); - assertEquals(1, id); - System.err.println("Server/Client Finished!"); + assertEquals(CLIENT_GLOBAL_OBJECT_ID, id); + + System.err.println("Finished test for: Server -> Client"); // normally this is in the 'connected', but we do it here, so that it's more linear and easier to debug try { - connection.getRemoteObject(SERVER_GLOBAL_OBJECT_ID, new RemoteObjectCallback() { + connection.getRemoteObject(SERVER_GLOBAL_OBJECT_ID, new RemoteObjectCallback() { @Override public - void created(final TestObject remoteObject) { - runTest(connection, globalRemoteServerObject, remoteObject, SERVER_GLOBAL_OBJECT_ID); + void created(final TestCow remoteObject) { + // MUST run on a separate thread because remote object method invocations are blocking + new Thread() { + @Override + public + void run() { + System.err.println("Running test for: Client -> Server"); + runTest(connection, globalRemoteServerObject, remoteObject, SERVER_GLOBAL_OBJECT_ID); + System.err.println("Done with test for: Client -> Server"); + } + }.start(); } }); } catch (IOException e) { @@ -287,21 +328,6 @@ class RmiGlobalTest extends BaseTest { waitForThreads(); } - private - interface TestObject extends Serializable { - void throwException(); - - void moo(); - - void moo(String value); - - void moo(String value, long delay); - - int id(); - - float slow(); - } - private static class ConnectionAware { private @@ -320,13 +346,13 @@ class RmiGlobalTest extends BaseTest { private static - class TestObjectImpl extends ConnectionAware implements TestObject { + class TestCowImpl extends ConnectionAware implements TestCow { public long value = System.currentTimeMillis(); public int moos; private final int id = 1; public - TestObjectImpl() { + TestCowImpl() { } @Override @@ -379,12 +405,4 @@ class RmiGlobalTest extends BaseTest { return 123.0F; } } - - - private static - class MessageWithTestObject implements RmiMessages { - public int number; - public String text; - public TestObject testObject; - } } diff --git a/test/dorkbox/network/rmi/RmiTest.java b/test/dorkbox/network/rmi/RmiTest.java index 687c2fad..dbc3fe9f 100644 --- a/test/dorkbox/network/rmi/RmiTest.java +++ b/test/dorkbox/network/rmi/RmiTest.java @@ -159,7 +159,7 @@ class RmiTest extends BaseTest { // Test sending a reference to a remote object. - MessageWithTestObject m = new MessageWithTestObject(); + MessageWithTestCow m = new MessageWithTestCow(); m.number = 678; m.text = "sometext"; m.testCow = test; @@ -172,7 +172,7 @@ class RmiTest extends BaseTest { public static void register(dorkbox.network.util.CryptoSerializationManager manager) { manager.register(Object.class); // Needed for Object#toString, hashCode, etc. - manager.register(MessageWithTestObject.class); + manager.register(MessageWithTestCow.class); manager.register(UnsupportedOperationException.class); } @@ -201,10 +201,10 @@ class RmiTest extends BaseTest { server.bind(false); final ListenerBridge listeners = server.listeners(); - listeners.add(new Listener.OnMessageReceived() { + listeners.add(new Listener.OnMessageReceived() { @Override public - void received(Connection connection, MessageWithTestObject m) { + void received(Connection connection, MessageWithTestCow m) { System.err.println("Received finish signal for test for: Client -> Server"); TestCow object = m.testCow; @@ -296,10 +296,10 @@ class RmiTest extends BaseTest { }); client.listeners() - .add(new Listener.OnMessageReceived() { + .add(new Listener.OnMessageReceived() { @Override public - void received(Connection connection, MessageWithTestObject m) { + void received(Connection connection, MessageWithTestCow m) { System.err.println("Received finish signal for test for: Client -> Server"); TestCow object = m.testCow; diff --git a/test/dorkbox/network/rmi/multiJVM/TestClient.java b/test/dorkbox/network/rmi/multiJVM/TestClient.java index 4e97c3c2..b19710a9 100644 --- a/test/dorkbox/network/rmi/multiJVM/TestClient.java +++ b/test/dorkbox/network/rmi/multiJVM/TestClient.java @@ -19,7 +19,7 @@ import dorkbox.network.connection.Connection; import dorkbox.network.connection.CryptoSerializationManager; import dorkbox.network.rmi.RemoteObjectCallback; import dorkbox.network.rmi.RmiTest; -import dorkbox.network.rmi.TestObject; +import dorkbox.network.rmi.TestCow; import io.netty.util.ResourceLeakDetector; /** @@ -41,8 +41,8 @@ class TestClient // rootLogger.setLevel(Level.OFF); - // rootLogger.setLevel(Level.DEBUG); - rootLogger.setLevel(Level.TRACE); + rootLogger.setLevel(Level.DEBUG); + // rootLogger.setLevel(Level.TRACE); // rootLogger.setLevel(Level.ALL); @@ -79,11 +79,13 @@ class TestClient Configuration configuration = new Configuration(); configuration.tcpPort = 2000; + configuration.udpPort = 2001; + configuration.udtPort = 2002; configuration.host = "localhost"; configuration.serialization = CryptoSerializationManager.DEFAULT(); RmiTest.register(configuration.serialization); - configuration.serialization.registerRmiInterface(TestObject.class); + configuration.serialization.registerRmiInterface(TestCow.class); try { @@ -99,16 +101,17 @@ class TestClient try { // if this is called in the dispatch thread, it will block network comms while waiting for a response and it won't work... - connection.getRemoteObject(TestObject.class, new RemoteObjectCallback() { + connection.getRemoteObject(TestCow.class, new RemoteObjectCallback() { @Override public - void created(final TestObject remoteObject) { + void created(final TestCow remoteObject) { new Thread() { @Override public void run() { // MUST run on a separate thread because remote object method invocations are blocking RmiTest.runTests(connection, remoteObject, 1); + System.err.println("DONE"); } }.start(); } @@ -117,18 +120,6 @@ class TestClient e.printStackTrace(); fail(); } - - // try { - // connection.getRemoteObject(TestObject.class, new RemoteObjectCallback() { - // @Override - // public - // void created(final TestObject remoteObject) { - // remoteObject.test(); - // } - // }); - // } catch (IOException e) { - // e.printStackTrace(); - // } } }); diff --git a/test/dorkbox/network/rmi/multiJVM/TestServer.java b/test/dorkbox/network/rmi/multiJVM/TestServer.java index 4e56d383..184c79e2 100644 --- a/test/dorkbox/network/rmi/multiJVM/TestServer.java +++ b/test/dorkbox/network/rmi/multiJVM/TestServer.java @@ -5,8 +5,8 @@ import java.io.IOException; import dorkbox.network.Server; import dorkbox.network.connection.CryptoSerializationManager; import dorkbox.network.rmi.RmiTest; -import dorkbox.network.rmi.TestObject; -import dorkbox.network.rmi.TestObjectImpl; +import dorkbox.network.rmi.TestCow; +import dorkbox.network.rmi.TestCowImpl; import dorkbox.util.exceptions.InitializationException; import dorkbox.util.exceptions.SecurityException; @@ -22,10 +22,12 @@ class TestServer dorkbox.network.Configuration configuration = new dorkbox.network.Configuration(); configuration.tcpPort = 2000; + configuration.udpPort = 2001; + configuration.udtPort = 2002; configuration.serialization = CryptoSerializationManager.DEFAULT(); RmiTest.register(configuration.serialization); - configuration.serialization.registerRmiImplementation(TestObject.class, TestObjectImpl.class); + configuration.serialization.registerRmiImplementation(TestCow.class, TestCowImpl.class); Server server = null; try { @@ -65,7 +67,7 @@ class TestServer // System.err.println("CONNECTED!"); // // try { - // TestObject object = connection.createProxyObject(TestObject.class); + // TestCow object = connection.createProxyObject(TestCow.class); // object.test(); // } catch (IOException e) { // e.printStackTrace();