* RMI system much more tolerant of bad data now (will display warning in log instead of crashing)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7040 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent 5faa1f08fe
commit 3087235def
  1. 21
      engine/src/desktop/com/jme3/app/AppletHarness.java
  2. 8
      engine/src/networking/com/jme3/network/rmi/MethodDef.java
  3. 12
      engine/src/networking/com/jme3/network/rmi/ObjectDef.java
  4. 54
      engine/src/networking/com/jme3/network/rmi/ObjectStore.java
  5. 12
      engine/src/networking/com/jme3/network/rmi/RemoteMethodCallMessage.java
  6. 8
      engine/src/networking/com/jme3/network/rmi/RemoteMethodReturnMessage.java
  7. 15
      engine/src/networking/com/jme3/network/rmi/RemoteObjectDefMessage.java
  8. 28
      engine/src/networking/com/jme3/network/rmi/RmiSerializer.java

@ -43,10 +43,13 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
/**
* @author Kirill
* @author Kirill Vainer
*/
public class AppletHarness extends Applet {
@ -70,12 +73,26 @@ public class AppletHarness extends Applet {
// load app cfg
if (appCfg != null){
InputStream in = null;
try {
InputStream in = appCfg.openStream();
in = appCfg.openStream();
settings.load(in);
in.close();
} catch (IOException ex){
// Called before application has been created ....
// Display error message through AWT
JOptionPane.showMessageDialog(this, "An error has occured while "
+ "loading applet configuration"
+ ex.getMessage(),
"jME3 Applet",
JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
} finally {
if (in != null)
try {
in.close();
} catch (IOException ex) {
}
}
}

@ -39,20 +39,20 @@ package com.jme3.network.rmi;
*
* @author Kirill Vainer
*/
class MethodDef {
public class MethodDef {
/**
* Method name
*/
String name;
public String name;
/**
* Return type
*/
Class<?> retType;
public Class<?> retType;
/**
* Parameter types
*/
Class<?>[] paramTypes;
public Class<?>[] paramTypes;
}

@ -37,33 +37,33 @@ import com.jme3.network.serializing.Serializable;
import java.lang.reflect.Method;
@Serializable
class ObjectDef {
public class ObjectDef {
/**
* The object name, can be null if undefined.
*/
String objectName;
public String objectName;
/**
* Object ID
*/
int objectId;
public int objectId;
/**
* Methods of the implementation on the local client. Set to null
* on remote clients.
*/
Method[] methods;
public Method[] methods;
/**
* Method definitions of the implementation. Set to null on
* the local client.
*/
MethodDef[] methodDefs;
public MethodDef[] methodDefs;
@Override
public String toString(){
return "ObjectDef[name=" + objectName + ", ID=" + objectId+"]";
return "ObjectDef[name=" + objectName + ", objectId=" + objectId+"]";
}
}

@ -42,25 +42,36 @@ import com.jme3.network.serializing.Serializer;
import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ObjectStore implements MessageListener, ConnectionListener {
private static final Logger logger = Logger.getLogger(ObjectStore.class.getName());
private static final class Invocation {
Object retVal;
boolean available = false;
@Override
public String toString(){
return "Invocation[" + retVal + "]";
}
}
private Client client;
private Server server;
// Local object ID counter
private short objectIdCounter = 0;
private volatile short objectIdCounter = 0;
// Local invocation ID counter
private short invocationIdCounter = 0;
private volatile short invocationIdCounter = 0;
// Invocations waiting ..
private IntMap<Invocation> pendingInvocations = new IntMap<Invocation>();
@ -120,10 +131,13 @@ public class ObjectStore implements MessageListener, ConnectionListener {
RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
defMsg.objects = new ObjectDef[]{ makeObjectDef(localObj) };
if (client != null)
if (client != null) {
client.send(defMsg);
else
logger.log(Level.INFO, "Client: Sending {0}", defMsg);
} else {
server.broadcast(defMsg);
logger.log(Level.INFO, "Server: Sending {0}", defMsg);
}
}
public <T> T getExposedObject(String name, Class<T> type, boolean waitFor) throws InterruptedException{
@ -140,7 +154,6 @@ public class ObjectStore implements MessageListener, ConnectionListener {
}
}
Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ type }, ro);
ro.loadMethods(type);
return (T) proxy;
@ -163,14 +176,17 @@ public class ObjectStore implements MessageListener, ConnectionListener {
if (needReturn){
call.invocationId = invocationIdCounter++;
invoke = new Invocation();
// Note: could cause threading issues if used from multiple threads
pendingInvocations.put(call.invocationId, invoke);
}
try{
if (server != null){
remoteObj.client.send(call);
logger.log(Level.INFO, "Server: Sending {0}", call);
}else{
client.send(call);
logger.log(Level.INFO, "Client: Sending {0}", call);
}
} catch (IOException ex){
ex.printStackTrace();
@ -186,6 +202,7 @@ public class ObjectStore implements MessageListener, ConnectionListener {
}
}
}
// Note: could cause threading issues if used from multiple threads
pendingInvocations.remove(call.invocationId);
return invoke.retVal;
}else{
@ -194,6 +211,9 @@ public class ObjectStore implements MessageListener, ConnectionListener {
}
public void messageReceived(Message message) {
// Might want to do more strict validation of the data
// in the message to prevent crashes
if (message instanceof RemoteObjectDefMessage){
RemoteObjectDefMessage defMsg = (RemoteObjectDefMessage) message;
@ -212,27 +232,38 @@ public class ObjectStore implements MessageListener, ConnectionListener {
}else if (message instanceof RemoteMethodCallMessage){
RemoteMethodCallMessage call = (RemoteMethodCallMessage) message;
LocalObject localObj = localObjects.get(call.objectId);
if (localObj == null)
return;
if (call.methodId < 0 || call.methodId >= localObj.methods.length)
return;
Object obj = localObj.theObject;
Method method = localObj.methods[call.methodId];
Object[] args = call.args;
Object ret;
Object ret = null;
try {
ret = method.invoke(obj, args);
} catch (Exception ex){
throw new RuntimeException(ex);
} catch (IllegalAccessException ex){
logger.log(Level.WARNING, "RMI: Error accessing method", ex);
} catch (IllegalArgumentException ex){
logger.log(Level.WARNING, "RMI: Invalid arguments", ex);
} catch (InvocationTargetException ex){
logger.log(Level.WARNING, "RMI: Invocation exception", ex);
}
if (method.getReturnType() != void.class){
// send return value back
RemoteMethodReturnMessage retMsg = new RemoteMethodReturnMessage();
retMsg.invocationID = invocationIdCounter++;
retMsg.invocationID = call.invocationId;
retMsg.retVal = ret;
try {
if (server != null){
call.getClient().send(retMsg);
logger.log(Level.INFO, "Server: Sending {0}", retMsg);
} else{
client.send(retMsg);
logger.log(Level.INFO, "Client: Sending {0}", retMsg);
}
} catch (IOException ex){
ex.printStackTrace();
@ -242,7 +273,8 @@ public class ObjectStore implements MessageListener, ConnectionListener {
RemoteMethodReturnMessage retMsg = (RemoteMethodReturnMessage) message;
Invocation invoke = pendingInvocations.get(retMsg.invocationID);
if (invoke == null){
throw new RuntimeException("Cannot find invocation ID: " + retMsg.invocationID);
logger.log(Level.WARNING, "Cannot find invocation ID: {0}", retMsg.invocationID);
return;
}
synchronized (invoke){
@ -268,8 +300,10 @@ public class ObjectStore implements MessageListener, ConnectionListener {
try {
if (this.client != null){
this.client.send(defMsg);
logger.log(Level.INFO, "Client: Sending {0}", defMsg);
} else{
client.send(defMsg);
logger.log(Level.INFO, "Server: Sending {0}", defMsg);
}
} catch (IOException ex){
ex.printStackTrace();

@ -41,7 +41,7 @@ import com.jme3.network.serializing.Serializable;
* @author Kirill Vainer
*/
@Serializable
class RemoteMethodCallMessage extends Message {
public class RemoteMethodCallMessage extends Message {
public RemoteMethodCallMessage(){
super(true);
@ -50,30 +50,30 @@ class RemoteMethodCallMessage extends Message {
/**
* The object ID on which the call is being made.
*/
int objectId;
public int objectId;
/**
* The method ID used for look-up in the LocalObject.methods array.
*/
short methodId;
public short methodId;
/**
* Invocation ID is used to identify a particular call if the calling
* client needs the return value of the called RMI method.
* This is set to zero if the method does not return a value.
*/
short invocationId;
public short invocationId;
/**
* Arguments of the remote method invocation.
*/
Object[] args;
public Object[] args;
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("MethodCall[objectID=").append(objectId).append(", methodID=")
sb.append("RemoteMethodCallMessage[objectID=").append(objectId).append(", methodID=")
.append(methodId);
if (args != null && args.length > 0){
sb.append(", args={");

@ -43,7 +43,7 @@ import com.jme3.network.serializing.Serializable;
* @author Kirill Vainer.
*/
@Serializable
class RemoteMethodReturnMessage extends Message {
public class RemoteMethodReturnMessage extends Message {
public RemoteMethodReturnMessage(){
super(true);
@ -52,16 +52,16 @@ class RemoteMethodReturnMessage extends Message {
/**
* Invocation ID that was set in the {@link RemoteMethodCallMessage}.
*/
short invocationID;
public short invocationID;
/**
* The return value, could be null.
*/
Object retVal;
public Object retVal;
@Override
public String toString(){
return "MethodReturn[ID="+invocationID+", Value="+retVal.toString()+"]";
return "RemoteMethodReturnMessage[ID="+invocationID+", Value="+retVal.toString()+"]";
}
}

@ -42,12 +42,23 @@ import com.jme3.network.serializing.Serializable;
* @author Kirill Vainer
*/
@Serializable
class RemoteObjectDefMessage extends Message {
public class RemoteObjectDefMessage extends Message {
ObjectDef[] objects;
public ObjectDef[] objects;
public RemoteObjectDefMessage(){
super(true);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("RemoteObjectDefMessage[\n");
for (ObjectDef def : objects){
sb.append("\t").append(def).append("\n");
}
sb.append("]");
return sb.toString();
}
}

@ -37,6 +37,8 @@ import com.jme3.network.serializing.SerializerRegistration;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* {@link RmiSerializer} is responsible for serializing RMI messages
@ -46,12 +48,17 @@ import java.nio.ByteBuffer;
*/
public class RmiSerializer extends Serializer {
private static final Logger logger = Logger.getLogger(RmiSerializer.class.getName());
// not good for multithread applications
private char[] chrBuf = new char[256];
private void writeString(ByteBuffer buffer, String string) throws IOException{
int length = string.length();
if (length > 255){
throw new IOException("Cannot serialize: "+ string + "\nToo long!");
logger.log(Level.WARNING, "The string length exceeds the limit! {0} > 255", length);
buffer.put( (byte) 0 );
return;
}
buffer.put( (byte) length );
@ -73,9 +80,10 @@ public class RmiSerializer extends Serializer {
buffer.putShort((short)0);
} else {
SerializerRegistration reg = Serializer.getSerializerRegistration(clazz);
if (reg == null)
throw new IOException("Unknown class: "+clazz);
if (reg == null){
logger.log(Level.WARNING, "Unknown class: {0}", clazz);
throw new IOException(); // prevents message from being serialized
}
buffer.putShort(reg.getId());
}
}
@ -85,10 +93,12 @@ public class RmiSerializer extends Serializer {
if (reg == null){
// either "void" or unknown val
short id = buffer.getShort(buffer.position()-2);
if (id == 0)
if (id == 0){
return void.class;
else
throw new IOException("Undefined class ID: " + id);
} else{
logger.log(Level.WARNING, "Undefined class ID: {0}", id);
throw new IOException(); // prevents message from being serialized
}
}
return reg.getType();
}
@ -173,6 +183,10 @@ public class RmiSerializer extends Serializer {
buffer.put((byte)0);
}else{
buffer.put((byte)call.args.length);
// Right now it writes 0 for every null argument
// and 1 for every non-null argument followed by the serialized
// argument. For the future, using a bit set should be considered.
for (Object obj : call.args){
if (obj != null){
buffer.put((byte)0x01);

Loading…
Cancel
Save