aboutsummaryrefslogtreecommitdiff
path: root/engine/src/networking/com/jme3/network/rmi/ObjectStore.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/networking/com/jme3/network/rmi/ObjectStore.java')
-rw-r--r--engine/src/networking/com/jme3/network/rmi/ObjectStore.java344
1 files changed, 344 insertions, 0 deletions
diff --git a/engine/src/networking/com/jme3/network/rmi/ObjectStore.java b/engine/src/networking/com/jme3/network/rmi/ObjectStore.java
new file mode 100644
index 0000000..137320d
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/rmi/ObjectStore.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.rmi;
+
+import com.jme3.network.ClientStateListener.DisconnectInfo;
+import com.jme3.network.*;
+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.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ObjectStore {
+
+ 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;
+
+ private ClientEventHandler clientEventHandler = new ClientEventHandler();
+ private ServerEventHandler serverEventHandler = new ServerEventHandler();
+
+ // Local object ID counter
+ private volatile short objectIdCounter = 0;
+
+ // Local invocation ID counter
+ private volatile short invocationIdCounter = 0;
+
+ // Invocations waiting ..
+ private IntMap<Invocation> pendingInvocations = new IntMap<Invocation>();
+
+ // Objects I share with other people
+ private IntMap<LocalObject> localObjects = new IntMap<LocalObject>();
+
+ // Objects others share with me
+ private HashMap<String, RemoteObject> remoteObjects = new HashMap<String, RemoteObject>();
+ private IntMap<RemoteObject> remoteObjectsById = new IntMap<RemoteObject>();
+
+ private final Object receiveObjectLock = new Object();
+
+ public class ServerEventHandler implements MessageListener<HostedConnection>,
+ ConnectionListener {
+
+ public void messageReceived(HostedConnection source, Message m) {
+ onMessage(source, m);
+ }
+
+ public void connectionAdded(Server server, HostedConnection conn) {
+ onConnection(conn);
+ }
+
+ public void connectionRemoved(Server server, HostedConnection conn) {
+ }
+
+ }
+
+ public class ClientEventHandler implements MessageListener,
+ ClientStateListener {
+
+ public void messageReceived(Object source, Message m) {
+ onMessage(null, m);
+ }
+
+ public void clientConnected(Client c) {
+ onConnection(null);
+ }
+
+ public void clientDisconnected(Client c, DisconnectInfo info) {
+ }
+
+ }
+
+ static {
+ Serializer s = new RmiSerializer();
+ Serializer.registerClass(RemoteObjectDefMessage.class, s);
+ Serializer.registerClass(RemoteMethodCallMessage.class, s);
+ Serializer.registerClass(RemoteMethodReturnMessage.class, s);
+ }
+
+ public ObjectStore(Client client) {
+ this.client = client;
+ client.addMessageListener(clientEventHandler,
+ RemoteObjectDefMessage.class,
+ RemoteMethodCallMessage.class,
+ RemoteMethodReturnMessage.class);
+ client.addClientStateListener(clientEventHandler);
+ }
+
+ public ObjectStore(Server server) {
+ this.server = server;
+ server.addMessageListener(serverEventHandler,
+ RemoteObjectDefMessage.class,
+ RemoteMethodCallMessage.class,
+ RemoteMethodReturnMessage.class);
+ server.addConnectionListener(serverEventHandler);
+ }
+
+ private ObjectDef makeObjectDef(LocalObject localObj){
+ ObjectDef def = new ObjectDef();
+ def.objectName = localObj.objectName;
+ def.objectId = localObj.objectId;
+ def.methods = localObj.methods;
+ return def;
+ }
+
+ public void exposeObject(String name, Object obj) throws IOException{
+ // Create a local object
+ LocalObject localObj = new LocalObject();
+ localObj.objectName = name;
+ localObj.objectId = objectIdCounter++;
+ localObj.theObject = obj;
+ //localObj.methods = obj.getClass().getMethods();
+
+ ArrayList<Method> methodList = new ArrayList<Method>();
+ for (Method method : obj.getClass().getMethods()){
+ if (method.getDeclaringClass() == obj.getClass()){
+ methodList.add(method);
+ }
+ }
+ localObj.methods = methodList.toArray(new Method[methodList.size()]);
+
+ // Put it in the store
+ localObjects.put(localObj.objectId, localObj);
+
+ // Inform the others of its existence
+ RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
+ defMsg.objects = new ObjectDef[]{ makeObjectDef(localObj) };
+
+ if (client != null) {
+ client.send(defMsg);
+ 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{
+ RemoteObject ro = remoteObjects.get(name);
+ if (ro == null){
+ if (!waitFor)
+ throw new RuntimeException("Cannot find remote object named: " + name);
+ else{
+ do {
+ synchronized (receiveObjectLock){
+ receiveObjectLock.wait();
+ }
+ } while ( (ro = remoteObjects.get(name)) == null );
+ }
+ }
+
+ Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ type }, ro);
+ ro.loadMethods(type);
+ return (T) proxy;
+ }
+
+ Object invokeRemoteMethod(RemoteObject remoteObj, Method method, Object[] args){
+ Integer methodIdInt = remoteObj.methodMap.get(method);
+ if (methodIdInt == null)
+ throw new RuntimeException("Method not implemented by remote object owner: "+method);
+
+ boolean needReturn = method.getReturnType() != void.class;
+ short objectId = remoteObj.objectId;
+ short methodId = methodIdInt.shortValue();
+ RemoteMethodCallMessage call = new RemoteMethodCallMessage();
+ call.methodId = methodId;
+ call.objectId = objectId;
+ call.args = args;
+
+ Invocation invoke = null;
+ if (needReturn){
+ call.invocationId = invocationIdCounter++;
+ invoke = new Invocation();
+ // Note: could cause threading issues if used from multiple threads
+ pendingInvocations.put(call.invocationId, invoke);
+ }
+
+ 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);
+ }
+
+ if (invoke != null){
+ synchronized(invoke){
+ while (!invoke.available){
+ try {
+ invoke.wait();
+ } catch (InterruptedException ex){
+ ex.printStackTrace();
+ }
+ }
+ }
+ // Note: could cause threading issues if used from multiple threads
+ pendingInvocations.remove(call.invocationId);
+ return invoke.retVal;
+ }else{
+ return null;
+ }
+ }
+
+ private void onMessage(HostedConnection source, 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;
+
+ ObjectDef[] defs = defMsg.objects;
+ for (ObjectDef def : defs){
+ RemoteObject remoteObject = new RemoteObject(this, source);
+ remoteObject.objectId = (short)def.objectId;
+ remoteObject.methodDefs = def.methodDefs;
+ remoteObjects.put(def.objectName, remoteObject);
+ remoteObjectsById.put(def.objectId, remoteObject);
+ }
+
+ synchronized (receiveObjectLock){
+ receiveObjectLock.notifyAll();
+ }
+ }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 = null;
+ try {
+ ret = method.invoke(obj, args);
+ } 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 = call.invocationId;
+ retMsg.retVal = ret;
+ if (server != null){
+ source.send(retMsg);
+ logger.log(Level.INFO, "Server: Sending {0}", retMsg);
+ } else{
+ client.send(retMsg);
+ logger.log(Level.INFO, "Client: Sending {0}", retMsg);
+ }
+ }
+ }else if (message instanceof RemoteMethodReturnMessage){
+ RemoteMethodReturnMessage retMsg = (RemoteMethodReturnMessage) message;
+ Invocation invoke = pendingInvocations.get(retMsg.invocationID);
+ if (invoke == null){
+ logger.log(Level.WARNING, "Cannot find invocation ID: {0}", retMsg.invocationID);
+ return;
+ }
+
+ synchronized (invoke){
+ invoke.retVal = retMsg.retVal;
+ invoke.available = true;
+ invoke.notifyAll();
+ }
+ }
+ }
+
+ private void onConnection(HostedConnection conn) {
+ if (localObjects.size() > 0){
+ // send a object definition message
+ ObjectDef[] defs = new ObjectDef[localObjects.size()];
+ int i = 0;
+ for (Entry<LocalObject> entry : localObjects){
+ defs[i] = makeObjectDef(entry.getValue());
+ i++;
+ }
+
+ RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
+ defMsg.objects = defs;
+ if (this.client != null){
+ this.client.send(defMsg);
+ logger.log(Level.INFO, "Client: Sending {0}", defMsg);
+ } else{
+ conn.send(defMsg);
+ logger.log(Level.INFO, "Server: Sending {0}", defMsg);
+ }
+ }
+ }
+
+}