Removed and reworked RMI so that local (in-jvm) connections have the
exact same behavior as network connections. This was to remove workarounds present when local in-jvm connections were used
This commit is contained in:
parent
d6e7affa1e
commit
ffc2375fe8
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.connection;
|
|
||||||
|
|
||||||
class ClassObject {
|
|
||||||
final Class<?> clazz;
|
|
||||||
final Object object;
|
|
||||||
|
|
||||||
public ClassObject(final Class<?> implementationClass, final Object remotePrimaryObject) {
|
|
||||||
clazz = implementationClass;
|
|
||||||
object = remotePrimaryObject;
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,6 +17,7 @@ package dorkbox.network.connection;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.AbstractMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
@ -42,7 +43,8 @@ import dorkbox.network.rmi.RemoteObjectCallback;
|
|||||||
import dorkbox.network.rmi.Rmi;
|
import dorkbox.network.rmi.Rmi;
|
||||||
import dorkbox.network.rmi.RmiBridge;
|
import dorkbox.network.rmi.RmiBridge;
|
||||||
import dorkbox.network.rmi.RmiRegistration;
|
import dorkbox.network.rmi.RmiRegistration;
|
||||||
import dorkbox.network.serialization.RmiSerializationManager;
|
import dorkbox.network.serialization.CryptoSerializationManager;
|
||||||
|
import dorkbox.util.ClassHelper;
|
||||||
import dorkbox.util.collections.IntMap;
|
import dorkbox.util.collections.IntMap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandler.Sharable;
|
import io.netty.channel.ChannelHandler.Sharable;
|
||||||
@ -110,7 +112,7 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
private final RmiBridge rmiBridge;
|
private final RmiBridge rmiBridge;
|
||||||
private final Map<Integer, RemoteObject> proxyIdCache = new WeakHashMap<Integer, RemoteObject>(8);
|
private final Map<Integer, RemoteObject> proxyIdCache = new WeakHashMap<Integer, RemoteObject>(8);
|
||||||
private final IntMap<RemoteObjectCallback> rmiRegistrationCallbacks = new IntMap<RemoteObjectCallback>();
|
private final IntMap<RemoteObjectCallback> rmiRegistrationCallbacks = new IntMap<RemoteObjectCallback>();
|
||||||
private int rmiRegistrationID = 0; // protected by synchronized (rmiRegistrationCallbacks)
|
private int rmiCallbackId = 0; // protected by synchronized (rmiRegistrationCallbacks)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All of the parameters can be null, when metaChannel wants to get the base class type
|
* All of the parameters can be null, when metaChannel wants to get the base class type
|
||||||
@ -902,11 +904,14 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
//
|
//
|
||||||
// RMI methods
|
// RMI methods
|
||||||
//
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings({"UnnecessaryLocalVariable", "unchecked", "Duplicates"})
|
@SuppressWarnings({"UnnecessaryLocalVariable", "unchecked", "Duplicates"})
|
||||||
@Override
|
@Override
|
||||||
public final
|
public final
|
||||||
<Iface> void getRemoteObject(final Class<Iface> interfaceClass, final RemoteObjectCallback<Iface> callback) {
|
<Iface> void createRemoteObject(final Class<Iface> interfaceClass, final RemoteObjectCallback<Iface> callback) {
|
||||||
if (!interfaceClass.isInterface()) {
|
if (!interfaceClass.isInterface()) {
|
||||||
throw new IllegalArgumentException("Cannot create a proxy for RMI access. It must be an interface.");
|
throw new IllegalArgumentException("Cannot create a proxy for RMI access. It must be an interface.");
|
||||||
}
|
}
|
||||||
@ -914,9 +919,9 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
RmiRegistration message;
|
RmiRegistration message;
|
||||||
|
|
||||||
synchronized (rmiRegistrationCallbacks) {
|
synchronized (rmiRegistrationCallbacks) {
|
||||||
int nextRmiID = rmiRegistrationID++;
|
int nextRmiCallbackId = rmiCallbackId++;
|
||||||
rmiRegistrationCallbacks.put(nextRmiID, callback);
|
rmiRegistrationCallbacks.put(nextRmiCallbackId, callback);
|
||||||
message = new RmiRegistration(interfaceClass, nextRmiID);
|
message = new RmiRegistration(interfaceClass, RmiBridge.INVALID_RMI, nextRmiCallbackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use a callback to notify us when the object is ready. We can't "create this on the fly" because we
|
// We use a callback to notify us when the object is ready. We can't "create this on the fly" because we
|
||||||
@ -932,10 +937,12 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
<Iface> void getRemoteObject(final int objectId, final RemoteObjectCallback<Iface> callback) {
|
<Iface> void getRemoteObject(final int objectId, final RemoteObjectCallback<Iface> callback) {
|
||||||
RmiRegistration message;
|
RmiRegistration message;
|
||||||
|
|
||||||
|
Class<?> iFaceClass = ClassHelper.getGenericParameterAsClassForSuperClass(RemoteObjectCallback.class, callback.getClass(), 0);
|
||||||
|
|
||||||
synchronized (rmiRegistrationCallbacks) {
|
synchronized (rmiRegistrationCallbacks) {
|
||||||
int nextRmiID = rmiRegistrationID++;
|
int nextRmiCallbackId = rmiCallbackId++;
|
||||||
rmiRegistrationCallbacks.put(nextRmiID, callback);
|
rmiRegistrationCallbacks.put(nextRmiCallbackId, callback);
|
||||||
message = new RmiRegistration(objectId, nextRmiID);
|
message = new RmiRegistration(iFaceClass, objectId, nextRmiCallbackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use a callback to notify us when the object is ready. We can't "create this on the fly" because we
|
// We use a callback to notify us when the object is ready. We can't "create this on the fly" because we
|
||||||
@ -945,68 +952,58 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
TCP(message).flush();
|
TCP(message).flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private
|
// default false
|
||||||
void collectRmiFields(final RmiBridge rmiBridge,
|
public static boolean ENABLE_PROXY_OBJECTS = true;
|
||||||
final LinkedList<ClassObject> classesToCheck, final ClassObject remoteClassObject, final Field[] fields) {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* For network connections, the interface class kryo ID == implementation class kryo ID, so they switch automatically.
|
||||||
|
* For local connections, we have to switch it appropriately in the LocalRmiProxy
|
||||||
|
*
|
||||||
|
* @param implementationClass
|
||||||
|
* @param callbackId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public final
|
||||||
|
RmiRegistration createNewRmiObject(final Class<?> interfaceClass, final Class<?> implementationClass, final int callbackId) {
|
||||||
|
|
||||||
final
|
CryptoSerializationManager manager = getEndPoint().serializationManager;
|
||||||
void registerInternal(final ConnectionImpl connection, final RmiRegistration remoteRegistration) {
|
|
||||||
final Class<?> interfaceClass = remoteRegistration.interfaceClass;
|
|
||||||
final int rmiID = remoteRegistration.rmiID;
|
|
||||||
|
|
||||||
if (interfaceClass != null) {
|
|
||||||
// THIS IS ON THE REMOTE CONNECTION (where the object will really exist as an implementation)
|
|
||||||
//
|
|
||||||
// CREATE a new ID, and register the ID and new object (must create a new one) in the object maps
|
|
||||||
|
|
||||||
// the interface class kryo ID == implementation class kryo ID, so they switcheroo automatically.
|
|
||||||
final Class<?> implementationClass = interfaceClass;
|
|
||||||
|
|
||||||
final RmiSerializationManager manager = getEndPoint().serializationManager;
|
|
||||||
|
|
||||||
KryoExtra kryo = null;
|
KryoExtra kryo = null;
|
||||||
final Object remotePrimaryObject;
|
Object object = null;
|
||||||
|
int rmiId = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
kryo = manager.takeKryo();
|
kryo = manager.takeKryo();
|
||||||
// this is what creates a new instance of the impl class, and stores it as an ID.
|
// this is what creates a new instance of the impl class, and stores it as an ID.
|
||||||
remotePrimaryObject = kryo.newInstance(implementationClass);
|
object = kryo.newInstance(implementationClass);
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Error creating RMI class " + implementationClass, e);
|
rmiId = rmiBridge.nextObjectId();
|
||||||
connection.TCP(new RmiRegistration(rmiID))
|
rmiBridge.register(rmiId, object);
|
||||||
.flush();
|
|
||||||
return;
|
|
||||||
} finally {
|
|
||||||
if (kryo != null) {
|
|
||||||
// we use kryo to create a new instance - so only return it on error or when it's done creating a new instance
|
|
||||||
manager.returnKryo(kryo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
rmiBridge.register(rmiBridge.nextObjectId(), remotePrimaryObject);
|
// the @Rmi annotation allows an RMI object to have fields with objects that are ALSO RMI objects
|
||||||
|
LinkedList<Map.Entry<Class<?>, Object>> classesToCheck = new LinkedList<Map.Entry<Class<?>, Object>>();
|
||||||
|
classesToCheck.add(new AbstractMap.SimpleEntry<Class<?>, Object>(implementationClass, object));
|
||||||
|
|
||||||
|
|
||||||
// the @Rmi annotation allows an RMI object to have fields with objects that are ALSO RMI
|
Map.Entry<Class<?>, Object> remoteClassObject;
|
||||||
LinkedList<ClassObject> classesToCheck = new LinkedList<ClassObject>();
|
|
||||||
classesToCheck.add(new ClassObject(implementationClass, remotePrimaryObject));
|
|
||||||
|
|
||||||
ClassObject remoteClassObject;
|
|
||||||
while (!classesToCheck.isEmpty()) {
|
while (!classesToCheck.isEmpty()) {
|
||||||
remoteClassObject = classesToCheck.removeFirst();
|
remoteClassObject = classesToCheck.removeFirst();
|
||||||
|
|
||||||
// we have to check the IMPLEMENTATION for any additional fields that will have proxy information.
|
// we have to check the IMPLEMENTATION for any additional fields that will have proxy information.
|
||||||
// we use getDeclaredFields() + walking the object hierarchy, so we get ALL the fields possible.
|
// we use getDeclaredFields() + walking the object hierarchy, so we get ALL the fields possible (public + private).
|
||||||
for (Field field : remoteClassObject.clazz.getDeclaredFields()) {
|
for (Field field : remoteClassObject.getKey()
|
||||||
|
.getDeclaredFields()) {
|
||||||
if (field.getAnnotation(Rmi.class) != null) {
|
if (field.getAnnotation(Rmi.class) != null) {
|
||||||
final Class<?> type = field.getType();
|
final Class<?> type = field.getType();
|
||||||
|
|
||||||
if (!type.isInterface()) {
|
if (!type.isInterface()) {
|
||||||
// the type must be an interface, otherwise RMI cannot create a proxy object
|
// the type must be an interface, otherwise RMI cannot create a proxy object
|
||||||
logger.error("Error checking RMI fields for: {}.{} -- It is not an interface!", remoteClassObject.clazz, field.getName());
|
logger.error("Error checking RMI fields for: {}.{} -- It is not an interface!",
|
||||||
|
remoteClassObject.getKey(),
|
||||||
|
field.getName());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,12 +1012,12 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
final Object o;
|
final Object o;
|
||||||
try {
|
try {
|
||||||
o = field.get(remoteClassObject.object);
|
o = field.get(remoteClassObject.getValue());
|
||||||
|
|
||||||
rmiBridge.register(rmiBridge.nextObjectId(), o);
|
rmiBridge.register(rmiBridge.nextObjectId(), o);
|
||||||
classesToCheck.add(new ClassObject(type, o));
|
classesToCheck.add(new AbstractMap.SimpleEntry<Class<?>, Object>(type, o));
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
logger.error("Error checking RMI fields for: {}.{}", remoteClassObject.clazz, field.getName(), e);
|
logger.error("Error checking RMI fields for: {}.{}", remoteClassObject.getKey(), field.getName(), e);
|
||||||
} finally {
|
} finally {
|
||||||
field.setAccessible(prev);
|
field.setAccessible(prev);
|
||||||
}
|
}
|
||||||
@ -1029,62 +1026,55 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
|
|
||||||
|
|
||||||
// have to check the object hierarchy as well
|
// have to check the object hierarchy as well
|
||||||
Class<?> superclass = remoteClassObject.clazz.getSuperclass();
|
Class<?> superclass = remoteClassObject.getKey()
|
||||||
|
.getSuperclass();
|
||||||
if (superclass != null && superclass != Object.class) {
|
if (superclass != null && superclass != Object.class) {
|
||||||
classesToCheck.add(new ClassObject(superclass, remoteClassObject.object));
|
classesToCheck.add(new AbstractMap.SimpleEntry<Class<?>, Object>(superclass, remoteClassObject.getValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.TCP(new RmiRegistration(remotePrimaryObject, rmiID)).flush();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error registering RMI class " + implementationClass, e);
|
logger.error("Error registering RMI class " + implementationClass, e);
|
||||||
connection.TCP(new RmiRegistration(rmiID)).flush();
|
} finally {
|
||||||
|
if (kryo != null) {
|
||||||
|
// we use kryo to create a new instance - so only return it on error or when it's done creating a new instance
|
||||||
|
manager.returnKryo(kryo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (remoteRegistration.remoteObjectId > RmiBridge.INVALID_RMI) {
|
|
||||||
// THIS IS ON THE REMOTE CONNECTION (where the object implementation will really exist)
|
|
||||||
//
|
|
||||||
// GET a LOCAL rmi object, if none get a specific, GLOBAL rmi object (objects that are not bound to a single connection).
|
|
||||||
Object object = getImplementationObject(remoteRegistration.remoteObjectId);
|
|
||||||
|
|
||||||
if (object != null) {
|
return new RmiRegistration(interfaceClass, rmiId, callbackId, object);
|
||||||
connection.TCP(new RmiRegistration(object, rmiID)).flush();
|
|
||||||
} else {
|
|
||||||
connection.TCP(new RmiRegistration(rmiID)).flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final
|
||||||
|
RmiRegistration getExistingRmiObject(final Class<?> interfaceClass, final int rmiId, final int callbackId) {
|
||||||
|
Object object = getImplementationObject(rmiId);
|
||||||
|
|
||||||
|
return new RmiRegistration(interfaceClass, rmiId, callbackId, object);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// THIS IS ON THE LOCAL CONNECTION SIDE, which is the side that called 'getRemoteObject()' This can be Server or Client.
|
|
||||||
|
|
||||||
// this will be null if there was an error
|
public final
|
||||||
Object remoteObject = remoteRegistration.remoteObject;
|
void runRmiCallback(final Class<?> interfaceClass, final int callbackId, final Object remoteObject) {
|
||||||
|
|
||||||
boolean noMoreRmiRemaining ;
|
|
||||||
RemoteObjectCallback callback;
|
RemoteObjectCallback callback;
|
||||||
|
|
||||||
synchronized (rmiRegistrationCallbacks) {
|
synchronized (rmiRegistrationCallbacks) {
|
||||||
callback = rmiRegistrationCallbacks.remove(remoteRegistration.rmiID);
|
callback = rmiRegistrationCallbacks.remove(callbackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
callback.created(remoteObject);
|
callback.created(remoteObject);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error getting remote object " + remoteObject.getClass() + ", ID: " + rmiID, e);
|
logger.error("Error getting or creating the remote object " + interfaceClass, e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by RMI
|
* Used by RMI by the LOCAL side when setting up the to fetch an object for the REMOTE side
|
||||||
*
|
*
|
||||||
* @return the registered ID for a specific object. This is used by the "client" side when setting up the to fetch an object for the
|
* @return the registered ID for a specific object.
|
||||||
* "service" side for RMI
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
<T> int getRegisteredId(final T object) {
|
<T> int getRegisteredId(final T object) {
|
||||||
// always check local before checking global, because less contention on the synchronization
|
// always check global before checking local, because less contention on the synchronization
|
||||||
RmiBridge globalRmiBridge = endPoint.globalRmiBridge;
|
RmiBridge globalRmiBridge = endPoint.globalRmiBridge;
|
||||||
|
|
||||||
if (globalRmiBridge == null) {
|
if (globalRmiBridge == null) {
|
||||||
@ -1100,7 +1090,7 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements ICryptoConn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by RMI for the CLIENT side, to get the proxy object as an interface
|
* Used by RMI for the LOCAL side, to get the proxy object as an interface
|
||||||
*
|
*
|
||||||
* @param objectID is the RMI object ID
|
* @param objectID is the RMI object ID
|
||||||
* @param iFace must be the interface the proxy will bind to
|
* @param iFace must be the interface the proxy will bind to
|
||||||
|
419
src/dorkbox/network/connection/RegisterRmiLocalHandler.java
Normal file
419
src/dorkbox/network/connection/RegisterRmiLocalHandler.java
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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.connection;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.KryoException;
|
||||||
|
import com.esotericsoftware.kryo.Serializer;
|
||||||
|
import com.esotericsoftware.kryo.util.IdentityMap;
|
||||||
|
|
||||||
|
import dorkbox.network.rmi.CachedMethod;
|
||||||
|
import dorkbox.network.rmi.InvokeMethod;
|
||||||
|
import dorkbox.network.rmi.RemoteObject;
|
||||||
|
import dorkbox.network.rmi.RmiBridge;
|
||||||
|
import dorkbox.network.rmi.RmiMessages;
|
||||||
|
import dorkbox.network.rmi.RmiProxyHandler;
|
||||||
|
import dorkbox.network.rmi.RmiRegistration;
|
||||||
|
import dorkbox.network.serialization.CryptoSerializationManager;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is for a local-connection (same-JVM) RMI method invocation
|
||||||
|
*
|
||||||
|
* Uses the "single writer principle" for fast access, but disregards 'single writer' for field cache, because duplicates are OK
|
||||||
|
* <p>
|
||||||
|
* This is for a LOCAL connection (same-JVM)
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
class RegisterRmiLocalHandler extends MessageToMessageDecoder<Object> {
|
||||||
|
private static final boolean ENABLE_PROXY_OBJECTS = ConnectionImpl.ENABLE_PROXY_OBJECTS;
|
||||||
|
private static final Field[] NO_REMOTE_FIELDS = new Field[0];
|
||||||
|
|
||||||
|
// private static final AtomicReferenceFieldUpdater<RegisterRmiLocalHandler, IdentityMap> rmiFieldsREF = AtomicReferenceFieldUpdater.newUpdater(
|
||||||
|
// RegisterRmiLocalHandler.class,
|
||||||
|
// IdentityMap.class,
|
||||||
|
// "fieldCache");
|
||||||
|
|
||||||
|
private static final AtomicReferenceFieldUpdater<RegisterRmiLocalHandler, IdentityMap> implToProxyREF = AtomicReferenceFieldUpdater.newUpdater(
|
||||||
|
RegisterRmiLocalHandler.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"implToProxy");
|
||||||
|
|
||||||
|
private static final AtomicReferenceFieldUpdater<RegisterRmiLocalHandler, IdentityMap> remoteObjectREF = AtomicReferenceFieldUpdater.newUpdater(
|
||||||
|
RegisterRmiLocalHandler.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"objectHasRemoteObjects");
|
||||||
|
|
||||||
|
// private volatile IdentityMap<Class<?>, Field[]> fieldCache = new IdentityMap<Class<?>, Field[]>();
|
||||||
|
private volatile IdentityMap<Object, Object> implToProxy = new IdentityMap<Object, Object>();
|
||||||
|
private volatile IdentityMap<Object, Field[]> objectHasRemoteObjects = new IdentityMap<Object, Field[]>();
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
RegisterRmiLocalHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected
|
||||||
|
void decode(final ChannelHandlerContext context, final Object msg, final List<Object> out) throws Exception {
|
||||||
|
ConnectionImpl connection = (ConnectionImpl) context.pipeline()
|
||||||
|
.last();
|
||||||
|
|
||||||
|
if (msg instanceof RmiRegistration) {
|
||||||
|
receivedRegistration(connection, (RmiRegistration) msg);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (msg instanceof InvokeMethod) {
|
||||||
|
InvokeMethod invokeMethod = (InvokeMethod) msg;
|
||||||
|
int methodClassID = invokeMethod.cachedMethod.methodClassID;
|
||||||
|
int methodIndex = invokeMethod.cachedMethod.methodIndex;
|
||||||
|
// have to replace the cached methods with the correct (remote) version, otherwise the wrong methods CAN BE invoked.
|
||||||
|
|
||||||
|
CryptoSerializationManager serialization = connection.getEndPoint()
|
||||||
|
.getSerialization();
|
||||||
|
|
||||||
|
|
||||||
|
CachedMethod cachedMethod;
|
||||||
|
try {
|
||||||
|
cachedMethod = serialization.getMethods(methodClassID)[methodIndex];
|
||||||
|
} catch (Exception ex) {
|
||||||
|
String errorMessage;
|
||||||
|
KryoExtra kryo = null;
|
||||||
|
try {
|
||||||
|
kryo = serialization.takeKryo();
|
||||||
|
|
||||||
|
Class<?> methodClass = kryo.getRegistration(methodClassID)
|
||||||
|
.getType();
|
||||||
|
|
||||||
|
errorMessage = "Invalid method index " + methodIndex + " for class: " + methodClass.getName();
|
||||||
|
} finally {
|
||||||
|
serialization.returnKryo(kryo);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new KryoException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Object[] args;
|
||||||
|
Serializer<?>[] serializers = cachedMethod.serializers;
|
||||||
|
|
||||||
|
int argStartIndex;
|
||||||
|
|
||||||
|
if (cachedMethod.overriddenMethod) {
|
||||||
|
// 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)
|
||||||
|
argStartIndex = 1;
|
||||||
|
|
||||||
|
args = new Object[serializers.length + 1];
|
||||||
|
args[0] = connection;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
argStartIndex = 0;
|
||||||
|
args = new Object[serializers.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0, n = serializers.length, j = argStartIndex; i < n; i++, j++) {
|
||||||
|
args[j] = invokeMethod.args[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// overwrite the invoke method fields with UPDATED versions that have the correct (remote side) implementation/args
|
||||||
|
invokeMethod.cachedMethod = cachedMethod;
|
||||||
|
invokeMethod.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedNormal(connection, msg, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
void receivedNormal(final ConnectionImpl connection, final Object msg, final List<Object> out) {
|
||||||
|
// else, this was "just a local message"
|
||||||
|
|
||||||
|
if (msg instanceof RmiMessages) {
|
||||||
|
// don't even process these message types
|
||||||
|
out.add(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// because we NORMALLY pass around just the object (there is no serialization going on...) we have to explicitly check to see
|
||||||
|
// if this object, or any of it's fields MIGHT HAVE BEEN an RMI Proxy (or should be on), and switcheroo it here.
|
||||||
|
// NORMALLY this is automatic since the kryo IDs on each side point to the "correct object" for serialization, but here we don't do that.
|
||||||
|
|
||||||
|
// maybe this object is supposed to switch to a proxy object?? (note: we cannot send proxy objects over local/network connections)
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
IdentityMap<Object, Object> implToProxy = implToProxyREF.get(this);
|
||||||
|
IdentityMap<Object, Field[]> objectHasRemoteObjects = remoteObjectREF.get(this);
|
||||||
|
|
||||||
|
|
||||||
|
Object proxy = implToProxy.get(msg);
|
||||||
|
if (proxy != null) {
|
||||||
|
// we have a proxy object. nothing left to do.
|
||||||
|
out.add(proxy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Class<?> messageClass = msg.getClass();
|
||||||
|
|
||||||
|
// are there any fields of this message class that COULD contain remote object fields? (NOTE: not RMI fields yet...)
|
||||||
|
final Field[] remoteObjectFields = objectHasRemoteObjects.get(messageClass);
|
||||||
|
if (remoteObjectFields == null) {
|
||||||
|
// maybe one of it's fields is a proxy object?
|
||||||
|
|
||||||
|
// we cache the fields that have to be replaced, so subsequent invocations are significantly more preformat
|
||||||
|
final ArrayList<Field> fields = new ArrayList<Field>();
|
||||||
|
|
||||||
|
// we have to walk the hierarchy of this object to check ALL fields, public and private, using getDeclaredFields()
|
||||||
|
while (messageClass != Object.class) {
|
||||||
|
// this will get ALL fields that are
|
||||||
|
for (Field field : messageClass.getDeclaredFields()) {
|
||||||
|
final Class<?> type = field.getType();
|
||||||
|
|
||||||
|
if (type.isInterface()) {
|
||||||
|
boolean prev = field.isAccessible();
|
||||||
|
final Object o;
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
o = field.get(msg);
|
||||||
|
|
||||||
|
if (o instanceof RemoteObject) {
|
||||||
|
RmiProxyHandler handler = (RmiProxyHandler) Proxy.getInvocationHandler(o);
|
||||||
|
|
||||||
|
int id = handler.objectID;
|
||||||
|
field.set(msg, connection.getImplementationObject(id));
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// is a field supposed to be a proxy?
|
||||||
|
proxy = implToProxy.get(o);
|
||||||
|
if (proxy != null) {
|
||||||
|
field.set(msg, proxy);
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
// logger.error("Error checking RMI fields for: {}.{}", remoteClassObject.getKey(), field.getName(), e);
|
||||||
|
} finally {
|
||||||
|
field.setAccessible(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messageClass = messageClass.getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
|
Field[] array;
|
||||||
|
if (fields.isEmpty()) {
|
||||||
|
// no need to ever process this class again.
|
||||||
|
array = NO_REMOTE_FIELDS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
array = fields.toArray(new Field[fields.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection SynchronizeOnNonFinalField
|
||||||
|
synchronized (objectHasRemoteObjects) {
|
||||||
|
// i know what I'm doing. This must be synchronized.
|
||||||
|
objectHasRemoteObjects.put(messageClass, array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (remoteObjectFields != NO_REMOTE_FIELDS) {
|
||||||
|
// quickly replace objects as necessary
|
||||||
|
|
||||||
|
for (Field field : remoteObjectFields) {
|
||||||
|
boolean prev = field.isAccessible();
|
||||||
|
final Object o;
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
o = field.get(msg);
|
||||||
|
|
||||||
|
if (o instanceof RemoteObject) {
|
||||||
|
RmiProxyHandler handler = (RmiProxyHandler) Proxy.getInvocationHandler(o);
|
||||||
|
|
||||||
|
int id = handler.objectID;
|
||||||
|
field.set(msg, connection.getImplementationObject(id));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// is a field supposed to be a proxy?
|
||||||
|
proxy = implToProxy.get(o);
|
||||||
|
if (proxy != null) {
|
||||||
|
field.set(msg, proxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
// logger.error("Error checking RMI fields for: {}.{}", remoteClassObject.getKey(), field.getName(), e);
|
||||||
|
} finally {
|
||||||
|
field.setAccessible(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.add(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private
|
||||||
|
void receivedRegistration(final ConnectionImpl connection, final RmiRegistration registration) {
|
||||||
|
// manage creating/getting/notifying this RMI object
|
||||||
|
|
||||||
|
// these fields are ALWAYS present!
|
||||||
|
final Class<?> interfaceClass = registration.interfaceClass;
|
||||||
|
final int callbackId = registration.callbackId;
|
||||||
|
if (registration.isRequest) {
|
||||||
|
// Check if we are creating a new REMOTE object. This check is always first.
|
||||||
|
if (registration.rmiId == RmiBridge.INVALID_RMI) {
|
||||||
|
// THIS IS ON THE REMOTE CONNECTION (where the object will really exist as an implementation)
|
||||||
|
//
|
||||||
|
// CREATE a new ID, and register the ID and new object (must create a new one) in the object maps
|
||||||
|
|
||||||
|
|
||||||
|
// have to convert the iFace -> Impl
|
||||||
|
EndPointBase<Connection> endPoint = connection.getEndPoint();
|
||||||
|
CryptoSerializationManager serialization = endPoint.getSerialization();
|
||||||
|
|
||||||
|
Class<?> rmiImpl = serialization.getRmiImpl(registration.interfaceClass);
|
||||||
|
|
||||||
|
RmiRegistration registrationResult = connection.createNewRmiObject(interfaceClass, rmiImpl, callbackId);
|
||||||
|
connection.TCP(registrationResult)
|
||||||
|
.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are getting an already existing REMOTE object. This check is always AFTER the check to create a new object
|
||||||
|
else {
|
||||||
|
// THIS IS ON THE REMOTE CONNECTION (where the object implementation will really exist)
|
||||||
|
//
|
||||||
|
// GET a LOCAL rmi object, if none get a specific, GLOBAL rmi object (objects that are not bound to a single connection).
|
||||||
|
RmiRegistration registrationResult = connection.getExistingRmiObject(interfaceClass, registration.rmiId, callbackId);
|
||||||
|
|
||||||
|
connection.TCP(registrationResult)
|
||||||
|
.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this is the response.
|
||||||
|
// THIS IS ON THE LOCAL CONNECTION SIDE, which is the side that called 'getRemoteObject()' This can be Server or Client.
|
||||||
|
|
||||||
|
|
||||||
|
// on "local" connections (as opposed to "network" connections), the objects ARE NOT serialized, so we never
|
||||||
|
// generate a proxy via the rmiID - so the LocalRmiProxy (in this use case) overwrites the remoteObject with a proxy object
|
||||||
|
// if we are the response, we want to create a proxy object instead of just passing the ACTUAL object.
|
||||||
|
// On the "network" (RemoteObjectSerializer.java) stack, this process is automatic -- and here we have to mimic this behavior.
|
||||||
|
|
||||||
|
// has to be 'registration.remoteObject' because we use it later on
|
||||||
|
if (ENABLE_PROXY_OBJECTS && registration.remoteObject != null) {
|
||||||
|
// override the implementation object with the proxy. This is required because RMI must be the same between "network" and "local"
|
||||||
|
// connections -- even if this "slows down" the speed/performance of what "local" connections offer.
|
||||||
|
RemoteObject proxyObject = connection.getProxyObject(registration.rmiId, interfaceClass);
|
||||||
|
|
||||||
|
// have to save A and B so we can correctly switch as necessary
|
||||||
|
//noinspection SynchronizeOnNonFinalField
|
||||||
|
synchronized (implToProxy) {
|
||||||
|
// i know what I'm doing. This must be synchronized.
|
||||||
|
implToProxy.put(registration.remoteObject, proxyObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.runRmiCallback(interfaceClass, callbackId, proxyObject);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
connection.runRmiCallback(interfaceClass, callbackId, registration.remoteObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
// LocalRmiClassEncoder replaceFieldObjects(final ConnectionImpl connection, final Object object, final Class<?> implClass) {
|
||||||
|
// Field[] rmiFields = fieldCache.get(implClass);
|
||||||
|
// int length = rmiFields.length;
|
||||||
|
//
|
||||||
|
// Object rmiObject = null;
|
||||||
|
// int[] rmiFieldIds = new int[length];
|
||||||
|
// for (int i = 0; i < length; i++) {
|
||||||
|
// Field field = rmiFields[i];
|
||||||
|
//
|
||||||
|
// // if it's an RMI object we want to write out a proxy object in the field instead of the actual object
|
||||||
|
// try {
|
||||||
|
// rmiObject = field.get(object);
|
||||||
|
// } catch (IllegalAccessException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (rmiObject == null) {
|
||||||
|
// rmiFieldIds[i] = 0; // 0 means it was null
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final Map<Object, Integer> localWeakCache = objectThreadLocals.get();
|
||||||
|
//
|
||||||
|
// Integer id = localWeakCache.get(rmiObject);
|
||||||
|
// if (id == null) {
|
||||||
|
// // duplicates are fine, as they represent the same object (as specified by the ID) on the remote side.
|
||||||
|
// int registeredId = connection.getRegisteredId(rmiObject);
|
||||||
|
// rmiFieldIds[i] = registeredId;
|
||||||
|
// localWeakCache.put(rmiObject, registeredId);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// rmiFieldIds[i] = id;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// LocalRmiClassEncoder localRmiClassEncoder = new LocalRmiClassEncoder();
|
||||||
|
// localRmiClassEncoder.rmiObject = object;
|
||||||
|
// localRmiClassEncoder.rmiFieldIds = rmiFieldIds;
|
||||||
|
//
|
||||||
|
// return localRmiClassEncoder;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Field[] getFields(final Class<?> clazz) {
|
||||||
|
// // duplicates are OK, because they will contain the same information, so we DO NOT care about single writers
|
||||||
|
//
|
||||||
|
// //noinspection unchecked
|
||||||
|
// final IdentityMap<Class<?>, Field[]> identityMap = rmiFieldsREF.get(this);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Field[] rmiFields = identityMap.get(clazz);
|
||||||
|
// if (rmiFields != null) {
|
||||||
|
// return rmiFields;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final ArrayList<Field> fields = new ArrayList<Field>();
|
||||||
|
//
|
||||||
|
// for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
// if (field.getAnnotation(Rmi.class) != null) {
|
||||||
|
// fields.add(field);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// rmiFields = new Field[fields.size()];
|
||||||
|
// fields.toArray(rmiFields);
|
||||||
|
//
|
||||||
|
// // save in cache
|
||||||
|
// fieldCache.put(clazz, rmiFields);
|
||||||
|
// rmiFieldsREF.lazySet(this, fieldCache);
|
||||||
|
//
|
||||||
|
// return rmiFields;
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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.connection;
|
||||||
|
|
||||||
|
import dorkbox.network.rmi.RmiBridge;
|
||||||
|
import dorkbox.network.rmi.RmiRegistration;
|
||||||
|
|
||||||
|
class RegisterRmiNetworkHandler implements Listener.OnMessageReceived<ConnectionImpl, RmiRegistration> {
|
||||||
|
|
||||||
|
RegisterRmiNetworkHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void received(final ConnectionImpl connection, final RmiRegistration registration) {
|
||||||
|
// manage creating/getting/notifying this RMI object
|
||||||
|
|
||||||
|
// these fields are ALWAYS present!
|
||||||
|
final Class<?> interfaceClass = registration.interfaceClass;
|
||||||
|
final int callbackId = registration.callbackId;
|
||||||
|
|
||||||
|
|
||||||
|
if (registration.isRequest) {
|
||||||
|
// Check if we are creating a new REMOTE object. This check is always first.
|
||||||
|
if (registration.rmiId == RmiBridge.INVALID_RMI) {
|
||||||
|
// THIS IS ON THE REMOTE CONNECTION (where the object will really exist as an implementation)
|
||||||
|
//
|
||||||
|
// CREATE a new ID, and register the ID and new object (must create a new one) in the object maps
|
||||||
|
|
||||||
|
// For network connections, the interface class kryo ID == implementation class kryo ID, so they switch automatically.
|
||||||
|
RmiRegistration registrationResult = connection.createNewRmiObject(interfaceClass, interfaceClass, callbackId);
|
||||||
|
connection.TCP(registrationResult).flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are getting an already existing REMOTE object. This check is always AFTER the check to create a new object
|
||||||
|
else {
|
||||||
|
// THIS IS ON THE REMOTE CONNECTION (where the object implementation will really exist)
|
||||||
|
//
|
||||||
|
// GET a LOCAL rmi object, if none get a specific, GLOBAL rmi object (objects that are not bound to a single connection).
|
||||||
|
RmiRegistration registrationResult = connection.getExistingRmiObject(interfaceClass, registration.rmiId, callbackId);
|
||||||
|
connection.TCP(registrationResult).flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this is the response.
|
||||||
|
// THIS IS ON THE LOCAL CONNECTION SIDE, which is the side that called 'getRemoteObject()' This can be Server or Client.
|
||||||
|
connection.runRmiCallback(interfaceClass, callbackId, registration.remoteObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.connection;
|
|
||||||
|
|
||||||
import dorkbox.network.rmi.RmiRegistration;
|
|
||||||
|
|
||||||
class RegisterRmiSystemListener implements Listener.OnMessageReceived<ConnectionImpl, RmiRegistration> {
|
|
||||||
|
|
||||||
RegisterRmiSystemListener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void received(final ConnectionImpl connection, final RmiRegistration registerClass) {
|
|
||||||
// register this into the RmiBridge
|
|
||||||
connection.registerInternal(connection, registerClass);
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,23 +18,18 @@ package dorkbox.network.connection.registration.local;
|
|||||||
import static dorkbox.network.connection.EndPointBase.maxShutdownWaitTimeInMilliSeconds;
|
import static dorkbox.network.connection.EndPointBase.maxShutdownWaitTimeInMilliSeconds;
|
||||||
|
|
||||||
import dorkbox.network.connection.Connection;
|
import dorkbox.network.connection.Connection;
|
||||||
|
import dorkbox.network.connection.RegisterRmiLocalHandler;
|
||||||
import dorkbox.network.connection.RegistrationWrapper;
|
import dorkbox.network.connection.RegistrationWrapper;
|
||||||
import dorkbox.network.connection.registration.MetaChannel;
|
import dorkbox.network.connection.registration.MetaChannel;
|
||||||
import dorkbox.network.connection.registration.RegistrationHandler;
|
import dorkbox.network.connection.registration.RegistrationHandler;
|
||||||
import dorkbox.network.pipeline.rmi.LocalRmiDecoder;
|
|
||||||
import dorkbox.network.pipeline.rmi.LocalRmiEncoder;
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
|
||||||
public abstract
|
public abstract
|
||||||
class RegistrationLocalHandler<C extends Connection> extends RegistrationHandler<C> {
|
class RegistrationLocalHandler<C extends Connection> extends RegistrationHandler<C> {
|
||||||
protected static final String LOCAL_RMI_ENCODER = "localRmiEncoder";
|
static final String LOCAL_RMI_HANDLER = "localRmiHandler";
|
||||||
protected static final String LOCAL_RMI_DECODER = "localRmiDecoder";
|
final RegisterRmiLocalHandler rmiLocalHandler = new RegisterRmiLocalHandler();
|
||||||
|
|
||||||
protected final LocalRmiEncoder encoder = new LocalRmiEncoder();
|
|
||||||
protected final LocalRmiDecoder decoder = new LocalRmiDecoder();
|
|
||||||
|
|
||||||
public
|
|
||||||
RegistrationLocalHandler(String name, RegistrationWrapper<C> registrationWrapper) {
|
RegistrationLocalHandler(String name, RegistrationWrapper<C> registrationWrapper) {
|
||||||
super(name, registrationWrapper);
|
super(name, registrationWrapper);
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,7 @@ class RegistrationLocalHandlerClient<C extends Connection> extends RegistrationL
|
|||||||
///////////////////////
|
///////////////////////
|
||||||
// DECODE (or upstream)
|
// DECODE (or upstream)
|
||||||
///////////////////////
|
///////////////////////
|
||||||
pipeline.addFirst(LOCAL_RMI_ENCODER, decoder);
|
pipeline.addFirst(LOCAL_RMI_HANDLER, rmiLocalHandler);
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// ENCODE (or downstream)
|
|
||||||
/////////////////////////
|
|
||||||
pipeline.addFirst(LOCAL_RMI_DECODER, encoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// have to setup connection handler
|
// have to setup connection handler
|
||||||
|
@ -83,13 +83,7 @@ class RegistrationLocalHandlerServer<C extends Connection> extends RegistrationL
|
|||||||
///////////////////////
|
///////////////////////
|
||||||
// DECODE (or upstream)
|
// DECODE (or upstream)
|
||||||
///////////////////////
|
///////////////////////
|
||||||
pipeline.addFirst(LOCAL_RMI_ENCODER, decoder);
|
pipeline.addFirst(LOCAL_RMI_HANDLER, rmiLocalHandler);
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// ENCODE (or downstream)
|
|
||||||
/////////////////////////
|
|
||||||
pipeline.addFirst(LOCAL_RMI_DECODER, encoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// have to setup connection handler
|
// have to setup connection handler
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.pipeline.rmi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is for a LOCAL connection (same-JVM)
|
|
||||||
*/
|
|
||||||
class LocalRmiClassEncoder {
|
|
||||||
public Object rmiObject;
|
|
||||||
|
|
||||||
// these are the "in order" rmi ID's for the fields of 'rmiObject'
|
|
||||||
public int[] rmiFieldIds;
|
|
||||||
|
|
||||||
public
|
|
||||||
LocalRmiClassEncoder() {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.pipeline.rmi;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import dorkbox.network.connection.ConnectionImpl;
|
|
||||||
import dorkbox.network.rmi.RemoteObject;
|
|
||||||
import dorkbox.network.serialization.CryptoSerializationManager;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is for a LOCAL connection (same-JVM)
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
class LocalRmiDecoder extends MessageToMessageDecoder<Object> {
|
|
||||||
|
|
||||||
private static final RmiFieldCache fieldCache = RmiFieldCache.INSTANCE();
|
|
||||||
|
|
||||||
public
|
|
||||||
LocalRmiDecoder() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void decode(final ChannelHandlerContext context, final Object msg, final List<Object> out) throws Exception {
|
|
||||||
if (msg instanceof LocalRmiClassEncoder) {
|
|
||||||
LocalRmiClassEncoder encoded = (LocalRmiClassEncoder) msg;
|
|
||||||
|
|
||||||
final Object messageObject = encoded.rmiObject;
|
|
||||||
final int[] rmiFieldIds = encoded.rmiFieldIds;
|
|
||||||
|
|
||||||
final Class<?> messageClass = messageObject.getClass();
|
|
||||||
ConnectionImpl connection = (ConnectionImpl) context.pipeline()
|
|
||||||
.last();
|
|
||||||
|
|
||||||
Object localRmiObject = null;
|
|
||||||
Field field;
|
|
||||||
int registeredId;
|
|
||||||
final Field[] rmiFields = fieldCache.get(messageClass);
|
|
||||||
for (int i = 0; i < rmiFields.length; i++) {
|
|
||||||
field = rmiFields[i];
|
|
||||||
registeredId = rmiFieldIds[i];
|
|
||||||
|
|
||||||
if (registeredId == 0) {
|
|
||||||
// the field was null/ignore
|
|
||||||
} else {
|
|
||||||
// if it's an RMI object we want to write out a proxy object in the field instead of the actual object
|
|
||||||
try {
|
|
||||||
localRmiObject = field.get(messageObject);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localRmiObject == null) {
|
|
||||||
throw new RuntimeException("Unable to get RMI interface object for RMI implementation");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CryptoSerializationManager serialization = connection.getEndPoint()
|
|
||||||
.getSerialization();
|
|
||||||
|
|
||||||
// TODO: see what's up with this.
|
|
||||||
final Class<?> iface = serialization.getRmiIface(localRmiObject.getClass());
|
|
||||||
if (iface == null) {
|
|
||||||
throw new RuntimeException("Unable to get interface for RMI implementation");
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoteObject remoteObject = connection.getProxyObject(registeredId, iface);
|
|
||||||
field.set(messageObject, remoteObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.add(messageObject);
|
|
||||||
} else {
|
|
||||||
out.add(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.pipeline.rmi;
|
|
||||||
|
|
||||||
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.EndPointBase;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is for a LOCAL connection (same-JVM)
|
|
||||||
*/
|
|
||||||
@Sharable
|
|
||||||
public
|
|
||||||
class LocalRmiEncoder extends MessageToMessageEncoder<Object> {
|
|
||||||
|
|
||||||
private static final Map<Class<?>, Boolean> transformObjectCache = new ConcurrentHashMap<Class<?>, Boolean>(EndPointBase.DEFAULT_THREAD_POOL_SIZE);
|
|
||||||
private static final RmiFieldCache fieldCache = RmiFieldCache.INSTANCE();
|
|
||||||
|
|
||||||
private final FastThreadLocal<Map<Object, Integer>> objectThreadLocals = new FastThreadLocal<Map<Object, Integer>>() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Map<Object, Integer> initialValue() {
|
|
||||||
return new WeakHashMap<Object, Integer>(8);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
LocalRmiEncoder() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
void encode(final ChannelHandlerContext context, final Object msg, final List<Object> out) throws Exception {
|
|
||||||
// have to change the rmi objects to proxy objects, with the server-assigned ID
|
|
||||||
// HOWEVER -- we cannot use the connection here (otherwise the logic is backwards). The connection must be set in other side
|
|
||||||
|
|
||||||
|
|
||||||
// we should check to see if this class is registered as having RMI methods present.
|
|
||||||
// if YES, then we have to send the corresponding RMI proxy object INSTEAD of the actual object.
|
|
||||||
// normally, it's OK to send the actual object.
|
|
||||||
//
|
|
||||||
// We specifically DO NOT do full serialization because it's not necessary --- we are running inside the same JVM.
|
|
||||||
final Class<?> implClass = msg.getClass();
|
|
||||||
Boolean needsTransform = transformObjectCache.get(implClass);
|
|
||||||
|
|
||||||
if (needsTransform == null) {
|
|
||||||
boolean hasRmi = implClass.getAnnotation(Rmi.class) != null;
|
|
||||||
|
|
||||||
if (hasRmi) {
|
|
||||||
// replace object
|
|
||||||
ConnectionImpl connection = (ConnectionImpl) context.pipeline()
|
|
||||||
.last();
|
|
||||||
out.add(replaceFieldObjects(connection, msg, implClass));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
transformObjectCache.put(implClass, Boolean.FALSE);
|
|
||||||
out.add(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (needsTransform) {
|
|
||||||
ConnectionImpl connection = (ConnectionImpl) context.pipeline()
|
|
||||||
.last();
|
|
||||||
// replace object
|
|
||||||
out.add(replaceFieldObjects(connection, msg, implClass));
|
|
||||||
} else {
|
|
||||||
out.add(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
Object replaceFieldObjects(final ConnectionImpl connection, final Object object, final Class<?> implClass) {
|
|
||||||
Field[] rmiFields = fieldCache.get(implClass);
|
|
||||||
int length = rmiFields.length;
|
|
||||||
|
|
||||||
Object rmiObject = null;
|
|
||||||
int[] rmiFieldIds = new int[length];
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
Field field = rmiFields[i];
|
|
||||||
|
|
||||||
// if it's an RMI object we want to write out a proxy object in the field instead of the actual object
|
|
||||||
try {
|
|
||||||
rmiObject = field.get(object);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rmiObject == null) {
|
|
||||||
rmiFieldIds[i] = 0; // 0 means it was null
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<Object, Integer> localWeakCache = objectThreadLocals.get();
|
|
||||||
|
|
||||||
Integer id = localWeakCache.get(rmiObject);
|
|
||||||
if (id == null) {
|
|
||||||
// duplicates are fine, as they represent the same object (as specified by the ID) on the remote side.
|
|
||||||
int registeredId = connection.getRegisteredId(rmiObject);
|
|
||||||
rmiFieldIds[i] = registeredId;
|
|
||||||
localWeakCache.put(rmiObject, registeredId);
|
|
||||||
} else {
|
|
||||||
rmiFieldIds[i] = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalRmiClassEncoder localRmiClassEncoder = new LocalRmiClassEncoder();
|
|
||||||
localRmiClassEncoder.rmiObject = object;
|
|
||||||
localRmiClassEncoder.rmiFieldIds = rmiFieldIds;
|
|
||||||
|
|
||||||
return localRmiClassEncoder;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016 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.pipeline.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
|
|
||||||
*
|
|
||||||
* This is for a LOCAL connection (same-JVM)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class RmiFieldCache {
|
|
||||||
private volatile IdentityMap<Class<?>, Field[]> fieldCache = new IdentityMap<Class<?>, Field[]>();
|
|
||||||
|
|
||||||
private static final AtomicReferenceFieldUpdater<RmiFieldCache, IdentityMap> rmiFieldsREF =
|
|
||||||
AtomicReferenceFieldUpdater.newUpdater(RmiFieldCache.class,
|
|
||||||
IdentityMap.class,
|
|
||||||
"fieldCache");
|
|
||||||
|
|
||||||
private static final RmiFieldCache INSTANCE = new RmiFieldCache();
|
|
||||||
public static synchronized RmiFieldCache INSTANCE() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
RmiFieldCache() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Field[] get(final Class<?> clazz) {
|
|
||||||
// duplicates are OK, because they will contain the same information, so we DO NOT care about single writers
|
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
final IdentityMap<Class<?>, Field[]> identityMap = rmiFieldsREF.get(this);
|
|
||||||
|
|
||||||
|
|
||||||
Field[] rmiFields = identityMap.get(clazz);
|
|
||||||
if (rmiFields != null) {
|
|
||||||
return rmiFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ArrayList<Field> fields = new ArrayList<Field>();
|
|
||||||
|
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
|
||||||
if (field.getAnnotation(Rmi.class) != null) {
|
|
||||||
fields.add(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
rmiFields = new Field[fields.size()];
|
|
||||||
fields.toArray(rmiFields);
|
|
||||||
|
|
||||||
// save in cache
|
|
||||||
fieldCache.put(clazz, rmiFields);
|
|
||||||
return rmiFields;
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,8 +22,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.lang.annotation.Target;
|
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
|
* This specifies to the RMI system that a specific field in this RMI object is ALSO an RMI object
|
||||||
* necessary.
|
|
||||||
*/
|
*/
|
||||||
@Retention(value = RetentionPolicy.RUNTIME)
|
@Retention(value = RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
|
@ -60,7 +60,7 @@ import dorkbox.util.collections.ObjectIntMap;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* <p/>
|
* <p/>
|
||||||
* Objects are {@link RmiSerializationManager#registerRmiInterface(Class)}, and endpoint connections can then {@link
|
* Objects are {@link RmiSerializationManager#registerRmiInterface(Class)}, and endpoint connections can then {@link
|
||||||
* Connection#getRemoteObject(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
|
||||||
* is not {@link RemoteObject#setAsync(boolean) ignored}, an extra byte is written. If the type of a parameter is not final (note that
|
* is not {@link RemoteObject#setAsync(boolean) ignored}, an extra byte is written. If the type of a parameter is not final (note that
|
||||||
@ -414,7 +414,7 @@ class RmiBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Warning. This is an advanced method. You should probably be using {@link Connection#getRemoteObject(Class, RemoteObjectCallback)}
|
* Warning. This is an advanced method. You should probably be using {@link Connection#createRemoteObject(Class, RemoteObjectCallback)}
|
||||||
* <p>
|
* <p>
|
||||||
* <p>
|
* <p>
|
||||||
* Returns a proxy object that implements the specified interfaces. Methods invoked on the proxy object will be invoked remotely on the
|
* Returns a proxy object that implements the specified interfaces. Methods invoked on the proxy object will be invoked remotely on the
|
||||||
@ -432,25 +432,29 @@ class RmiBridge {
|
|||||||
* have the proxy object replaced with the registered object.
|
* have the proxy object replaced with the registered object.
|
||||||
*
|
*
|
||||||
* @see RemoteObject
|
* @see RemoteObject
|
||||||
|
*
|
||||||
|
* @param connection this is really the network client -- there is ONLY ever 1 connection
|
||||||
|
* @param objectID this is the remote object ID (assigned by RMI). This is NOT the kryo registration ID
|
||||||
|
* @param iFace this is the RMI interface
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
RemoteObject createProxyObject(Connection connection, int objectID, Class<?> iface) {
|
RemoteObject createProxyObject(Connection connection, int objectID, Class<?> iFace) {
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
throw new IllegalArgumentException("connection cannot be null.");
|
throw new IllegalArgumentException("connection cannot be null.");
|
||||||
}
|
}
|
||||||
if (iface == null) {
|
if (iFace == null) {
|
||||||
throw new IllegalArgumentException("iface cannot be null.");
|
throw new IllegalArgumentException("iface cannot be null.");
|
||||||
}
|
}
|
||||||
if (!iface.isInterface()) {
|
if (!iFace.isInterface()) {
|
||||||
throw new IllegalArgumentException("iface must be an interface.");
|
throw new IllegalArgumentException("iface must be an interface.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?>[] temp = new Class<?>[2];
|
Class<?>[] temp = new Class<?>[2];
|
||||||
temp[0] = RemoteObject.class;
|
temp[0] = RemoteObject.class;
|
||||||
temp[1] = iface;
|
temp[1] = iFace;
|
||||||
|
|
||||||
return (RemoteObject) Proxy.newProxyInstance(RmiBridge.class.getClassLoader(),
|
return (RemoteObject) Proxy.newProxyInstance(RmiBridge.class.getClassLoader(),
|
||||||
temp,
|
temp,
|
||||||
new RmiProxyHandler(connection, objectID, iface));
|
new RmiProxyHandler(connection, objectID, iFace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ import dorkbox.network.serialization.RmiSerializationManager;
|
|||||||
* <p>
|
* <p>
|
||||||
* The only methods than can be invoked are INTERFACE methods and OBJECT methods
|
* The only methods than can be invoked are INTERFACE methods and OBJECT methods
|
||||||
*/
|
*/
|
||||||
|
public
|
||||||
class RmiProxyHandler implements InvocationHandler {
|
class RmiProxyHandler implements InvocationHandler {
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
|
|
||||||
@ -66,7 +67,6 @@ class RmiProxyHandler implements InvocationHandler {
|
|||||||
private final boolean[] pendingResponses = new boolean[64];
|
private final boolean[] pendingResponses = new boolean[64];
|
||||||
|
|
||||||
private final Connection connection;
|
private final Connection connection;
|
||||||
private final Class<?> iFace;
|
|
||||||
public final int objectID; // this is the RMI id
|
public final int objectID; // this is the RMI id
|
||||||
public final int ID; // this is the KRYO id
|
public final int ID; // this is the KRYO id
|
||||||
|
|
||||||
@ -96,7 +96,6 @@ class RmiProxyHandler implements InvocationHandler {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.iFace = iFace;
|
|
||||||
this.objectID = objectID;
|
this.objectID = objectID;
|
||||||
this.proxyString = "<proxy #" + objectID + ">";
|
this.proxyString = "<proxy #" + objectID + ">";
|
||||||
|
|
||||||
|
@ -20,13 +20,27 @@ package dorkbox.network.rmi;
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class RmiRegistration {
|
class RmiRegistration {
|
||||||
|
public boolean isRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this is null if there are problems creating an object on the remote side, otherwise it is non-null.
|
||||||
|
*/
|
||||||
public Object remoteObject;
|
public Object remoteObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this is used to create a NEW rmi object on the REMOTE side (these are bound the to connection. They are NOT GLOBAL, ie: available on all connections)
|
||||||
|
*/
|
||||||
public Class<?> interfaceClass;
|
public Class<?> interfaceClass;
|
||||||
|
|
||||||
// this is used to get specific, GLOBAL rmi objects (objects that are not bound to a single connection)
|
/**
|
||||||
public int remoteObjectId;
|
* this is used to get specific, GLOBAL rmi objects (objects that are not bound to a single connection)
|
||||||
|
*/
|
||||||
|
public int rmiId;
|
||||||
|
|
||||||
public int rmiID;
|
/**
|
||||||
|
* this is the callback ID assigned by the LOCAL side, to know WHICH RMI callback to call when we have a remote object available
|
||||||
|
*/
|
||||||
|
public int callbackId;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private
|
private
|
||||||
@ -34,35 +48,35 @@ class RmiRegistration {
|
|||||||
// for serialization
|
// for serialization
|
||||||
}
|
}
|
||||||
|
|
||||||
// When requesting a new remote object to be created.
|
/**
|
||||||
// SENT FROM "client" -> "server"
|
* When requesting a new or existing remote object
|
||||||
|
* SENT FROM "local" -> "remote"
|
||||||
|
*
|
||||||
|
* @param interfaceClass the class to create
|
||||||
|
* @param rmiId the RMI id to get from the REMOTE side
|
||||||
|
* @param callbackId the rmi callback ID on the LOCAL side, to know which callback to use
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
RmiRegistration(final Class<?> interfaceClass, final int rmiID) {
|
RmiRegistration(final Class<?> interfaceClass, final int rmiId, final int callbackId) {
|
||||||
|
isRequest = true;
|
||||||
this.interfaceClass = interfaceClass;
|
this.interfaceClass = interfaceClass;
|
||||||
this.rmiID = rmiID;
|
this.rmiId = rmiId;
|
||||||
}
|
this.callbackId = callbackId;
|
||||||
|
|
||||||
// When requesting a new remote object to be created.
|
|
||||||
// SENT FROM "client" -> "server"
|
|
||||||
public
|
|
||||||
RmiRegistration(final int remoteObjectId, final int rmiID) {
|
|
||||||
this.remoteObjectId = remoteObjectId;
|
|
||||||
this.rmiID = rmiID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// When there was an error creating the remote object.
|
/**
|
||||||
// SENT FROM "server" -> "client"
|
* This is when we successfully created a new object (if there was an error, remoteObject is null)
|
||||||
|
* SENT FROM "remote" -> "local"
|
||||||
|
*
|
||||||
|
* @param callbackId the rmi callback ID on the LOCAL side, to know which callback to use
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
RmiRegistration(final int rmiID) {
|
RmiRegistration(final Class<?> interfaceClass, final int rmiId, final int callbackId, final Object remoteObject) {
|
||||||
this.rmiID = rmiID;
|
isRequest = false;
|
||||||
}
|
this.interfaceClass = interfaceClass;
|
||||||
|
this.rmiId = rmiId;
|
||||||
// This is when we successfully created a new object
|
this.callbackId = callbackId;
|
||||||
// SENT FROM "server" -> "client"
|
|
||||||
public
|
|
||||||
RmiRegistration(final Object remoteObject, final int rmiID) {
|
|
||||||
this.remoteObject = remoteObject;
|
this.remoteObject = remoteObject;
|
||||||
this.rmiID = rmiID;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ import java.io.IOException;
|
|||||||
*
|
*
|
||||||
* @author Nathan Sweet <misc@n4te.com>
|
* @author Nathan Sweet <misc@n4te.com>
|
||||||
* @see dorkbox.network.connection.Connection#getRemoteObject(int, RemoteObjectCallback)
|
* @see dorkbox.network.connection.Connection#getRemoteObject(int, RemoteObjectCallback)
|
||||||
* @see dorkbox.network.connection.Connection#getRemoteObject(Class, RemoteObjectCallback)
|
* @see dorkbox.network.connection.Connection#createRemoteObject(Class, RemoteObjectCallback)
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class TimeoutException extends IOException {
|
class TimeoutException extends IOException {
|
||||||
|
@ -261,8 +261,8 @@ class Serialization implements CryptoSerializationManager, RmiSerializationManag
|
|||||||
private RemoteObjectSerializer remoteObjectSerializer;
|
private RemoteObjectSerializer remoteObjectSerializer;
|
||||||
|
|
||||||
// used to track which interface -> implementation, for use by RMI
|
// used to track which interface -> implementation, for use by RMI
|
||||||
private final IntMap<Class<?>> rmiIdToImpl = new IntMap<Class<?>>();
|
private final IntMap<Class<?>> rmiKryoIdToImpl = new IntMap<Class<?>>();
|
||||||
private final IntMap<Class<?>> rmiIdToIface = 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<?>>();
|
||||||
@ -380,8 +380,8 @@ class Serialization implements CryptoSerializationManager, RmiSerializationManag
|
|||||||
|
|
||||||
// sets up the RMI, so when we receive the iface class from the client, we know what impl to use
|
// sets up the RMI, so when we receive the iface class from the client, we know what impl to use
|
||||||
// if this is over-written, we don't care.
|
// if this is over-written, we don't care.
|
||||||
rmiIdToImpl.put(id, remoteImplClass.implClass); // the "server" translates the ID back to the impl on kryo read
|
rmiKryoIdToImpl.put(id, remoteImplClass.implClass); // the "server" translates the ID back to the impl on kryo read
|
||||||
rmiIdToIface.put(id, remoteImplClass.ifaceClass); // the "server" translates the ID to the iface on kryo write
|
rmiKryoIdToIface.put(id, remoteImplClass.ifaceClass); // the "server" translates the ID to the iface on kryo write
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,8 +575,8 @@ class Serialization implements CryptoSerializationManager, RmiSerializationManag
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
Class<?> getRmiIface(Class<?> implementation) {
|
Class<?> getRmiImpl(Class<?> iface) {
|
||||||
return rmiImplToIface.get(implementation);
|
return rmiIfaceToImpl.get(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -804,6 +804,12 @@ class Serialization implements CryptoSerializationManager, RmiSerializationManag
|
|||||||
return methodCache.get(classId);
|
return methodCache.get(classId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
boolean isRmiEnabled() {
|
||||||
|
return usesRmi;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized
|
public synchronized
|
||||||
boolean initialized() {
|
boolean initialized() {
|
||||||
|
Loading…
Reference in New Issue
Block a user