• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.jme3.network.rmi;
34 
35 import com.jme3.network.ClientStateListener.DisconnectInfo;
36 import com.jme3.network.*;
37 import com.jme3.network.serializing.Serializer;
38 import com.jme3.util.IntMap;
39 import com.jme3.util.IntMap.Entry;
40 import java.io.IOException;
41 import java.lang.reflect.InvocationTargetException;
42 import java.lang.reflect.Method;
43 import java.lang.reflect.Proxy;
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
48 
49 public class ObjectStore {
50 
51     private static final Logger logger = Logger.getLogger(ObjectStore.class.getName());
52 
53     private static final class Invocation {
54 
55         Object retVal;
56         boolean available = false;
57 
58         @Override
toString()59         public String toString(){
60             return "Invocation[" + retVal + "]";
61         }
62     }
63 
64     private Client client;
65     private Server server;
66 
67     private ClientEventHandler clientEventHandler = new ClientEventHandler();
68     private ServerEventHandler serverEventHandler = new ServerEventHandler();
69 
70     // Local object ID counter
71     private volatile short objectIdCounter = 0;
72 
73     // Local invocation ID counter
74     private volatile short invocationIdCounter = 0;
75 
76     // Invocations waiting ..
77     private IntMap<Invocation> pendingInvocations = new IntMap<Invocation>();
78 
79     // Objects I share with other people
80     private IntMap<LocalObject> localObjects = new IntMap<LocalObject>();
81 
82     // Objects others share with me
83     private HashMap<String, RemoteObject> remoteObjects = new HashMap<String, RemoteObject>();
84     private IntMap<RemoteObject> remoteObjectsById = new IntMap<RemoteObject>();
85 
86     private final Object receiveObjectLock = new Object();
87 
88     public class ServerEventHandler implements MessageListener<HostedConnection>,
89                                                       ConnectionListener {
90 
messageReceived(HostedConnection source, Message m)91         public void messageReceived(HostedConnection source, Message m) {
92             onMessage(source, m);
93         }
94 
connectionAdded(Server server, HostedConnection conn)95         public void connectionAdded(Server server, HostedConnection conn) {
96             onConnection(conn);
97         }
98 
connectionRemoved(Server server, HostedConnection conn)99         public void connectionRemoved(Server server, HostedConnection conn) {
100         }
101 
102     }
103 
104     public class ClientEventHandler implements MessageListener,
105                                                       ClientStateListener {
106 
messageReceived(Object source, Message m)107         public void messageReceived(Object source, Message m) {
108             onMessage(null, m);
109         }
110 
clientConnected(Client c)111         public void clientConnected(Client c) {
112             onConnection(null);
113         }
114 
clientDisconnected(Client c, DisconnectInfo info)115         public void clientDisconnected(Client c, DisconnectInfo info) {
116         }
117 
118     }
119 
120     static {
121         Serializer s = new RmiSerializer();
Serializer.registerClass(RemoteObjectDefMessage.class, s)122         Serializer.registerClass(RemoteObjectDefMessage.class, s);
Serializer.registerClass(RemoteMethodCallMessage.class, s)123         Serializer.registerClass(RemoteMethodCallMessage.class, s);
Serializer.registerClass(RemoteMethodReturnMessage.class, s)124         Serializer.registerClass(RemoteMethodReturnMessage.class, s);
125     }
126 
ObjectStore(Client client)127     public ObjectStore(Client client) {
128         this.client = client;
129         client.addMessageListener(clientEventHandler,
130                 RemoteObjectDefMessage.class,
131                 RemoteMethodCallMessage.class,
132                 RemoteMethodReturnMessage.class);
133         client.addClientStateListener(clientEventHandler);
134     }
135 
ObjectStore(Server server)136     public ObjectStore(Server server) {
137         this.server = server;
138         server.addMessageListener(serverEventHandler,
139                 RemoteObjectDefMessage.class,
140                 RemoteMethodCallMessage.class,
141                 RemoteMethodReturnMessage.class);
142         server.addConnectionListener(serverEventHandler);
143     }
144 
makeObjectDef(LocalObject localObj)145     private ObjectDef makeObjectDef(LocalObject localObj){
146         ObjectDef def = new ObjectDef();
147         def.objectName = localObj.objectName;
148         def.objectId   = localObj.objectId;
149         def.methods    = localObj.methods;
150         return def;
151     }
152 
exposeObject(String name, Object obj)153     public void exposeObject(String name, Object obj) throws IOException{
154         // Create a local object
155         LocalObject localObj = new LocalObject();
156         localObj.objectName = name;
157         localObj.objectId  = objectIdCounter++;
158         localObj.theObject = obj;
159         //localObj.methods   = obj.getClass().getMethods();
160 
161         ArrayList<Method> methodList = new ArrayList<Method>();
162         for (Method method : obj.getClass().getMethods()){
163             if (method.getDeclaringClass() == obj.getClass()){
164                 methodList.add(method);
165             }
166         }
167         localObj.methods = methodList.toArray(new Method[methodList.size()]);
168 
169         // Put it in the store
170         localObjects.put(localObj.objectId, localObj);
171 
172         // Inform the others of its existence
173         RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
174         defMsg.objects = new ObjectDef[]{ makeObjectDef(localObj) };
175 
176         if (client != null) {
177             client.send(defMsg);
178             logger.log(Level.INFO, "Client: Sending {0}", defMsg);
179         } else {
180             server.broadcast(defMsg);
181             logger.log(Level.INFO, "Server: Sending {0}", defMsg);
182         }
183     }
184 
getExposedObject(String name, Class<T> type, boolean waitFor)185     public <T> T getExposedObject(String name, Class<T> type, boolean waitFor) throws InterruptedException{
186         RemoteObject ro = remoteObjects.get(name);
187         if (ro == null){
188             if (!waitFor)
189                 throw new RuntimeException("Cannot find remote object named: " + name);
190             else{
191                 do {
192                     synchronized (receiveObjectLock){
193                         receiveObjectLock.wait();
194                     }
195                 } while ( (ro = remoteObjects.get(name)) == null );
196             }
197         }
198 
199         Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ type }, ro);
200         ro.loadMethods(type);
201         return (T) proxy;
202     }
203 
invokeRemoteMethod(RemoteObject remoteObj, Method method, Object[] args)204     Object invokeRemoteMethod(RemoteObject remoteObj, Method method, Object[] args){
205         Integer methodIdInt = remoteObj.methodMap.get(method);
206         if (methodIdInt == null)
207              throw new RuntimeException("Method not implemented by remote object owner: "+method);
208 
209         boolean needReturn = method.getReturnType() != void.class;
210         short objectId = remoteObj.objectId;
211         short methodId = methodIdInt.shortValue();
212         RemoteMethodCallMessage call = new RemoteMethodCallMessage();
213         call.methodId = methodId;
214         call.objectId = objectId;
215         call.args = args;
216 
217         Invocation invoke = null;
218         if (needReturn){
219             call.invocationId = invocationIdCounter++;
220             invoke = new Invocation();
221             // Note: could cause threading issues if used from multiple threads
222             pendingInvocations.put(call.invocationId, invoke);
223         }
224 
225         if (server != null){
226             remoteObj.client.send(call);
227             logger.log(Level.INFO, "Server: Sending {0}", call);
228         }else{
229             client.send(call);
230             logger.log(Level.INFO, "Client: Sending {0}", call);
231         }
232 
233         if (invoke != null){
234             synchronized(invoke){
235                 while (!invoke.available){
236                     try {
237                         invoke.wait();
238                     } catch (InterruptedException ex){
239                         ex.printStackTrace();
240                     }
241                 }
242             }
243             // Note: could cause threading issues if used from multiple threads
244             pendingInvocations.remove(call.invocationId);
245             return invoke.retVal;
246         }else{
247             return null;
248         }
249     }
250 
onMessage(HostedConnection source, Message message)251     private void onMessage(HostedConnection source, Message message) {
252         // Might want to do more strict validation of the data
253         // in the message to prevent crashes
254 
255         if (message instanceof RemoteObjectDefMessage){
256             RemoteObjectDefMessage defMsg = (RemoteObjectDefMessage) message;
257 
258             ObjectDef[] defs = defMsg.objects;
259             for (ObjectDef def : defs){
260                 RemoteObject remoteObject = new RemoteObject(this, source);
261                 remoteObject.objectId = (short)def.objectId;
262                 remoteObject.methodDefs = def.methodDefs;
263                 remoteObjects.put(def.objectName, remoteObject);
264                 remoteObjectsById.put(def.objectId, remoteObject);
265             }
266 
267             synchronized (receiveObjectLock){
268                 receiveObjectLock.notifyAll();
269             }
270         }else if (message instanceof RemoteMethodCallMessage){
271             RemoteMethodCallMessage call = (RemoteMethodCallMessage) message;
272             LocalObject localObj = localObjects.get(call.objectId);
273             if (localObj == null)
274                 return;
275 
276             if (call.methodId < 0 || call.methodId >= localObj.methods.length)
277                 return;
278 
279             Object obj = localObj.theObject;
280             Method method = localObj.methods[call.methodId];
281             Object[] args = call.args;
282             Object ret = null;
283             try {
284                 ret = method.invoke(obj, args);
285             } catch (IllegalAccessException ex){
286                 logger.log(Level.WARNING, "RMI: Error accessing method", ex);
287             } catch (IllegalArgumentException ex){
288                 logger.log(Level.WARNING, "RMI: Invalid arguments", ex);
289             } catch (InvocationTargetException ex){
290                 logger.log(Level.WARNING, "RMI: Invocation exception", ex);
291             }
292 
293             if (method.getReturnType() != void.class){
294                 // send return value back
295                 RemoteMethodReturnMessage retMsg = new RemoteMethodReturnMessage();
296                 retMsg.invocationID = call.invocationId;
297                 retMsg.retVal = ret;
298                 if (server != null){
299                     source.send(retMsg);
300                     logger.log(Level.INFO, "Server: Sending {0}", retMsg);
301                 } else{
302                     client.send(retMsg);
303                     logger.log(Level.INFO, "Client: Sending {0}", retMsg);
304                 }
305             }
306         }else if (message instanceof RemoteMethodReturnMessage){
307             RemoteMethodReturnMessage retMsg = (RemoteMethodReturnMessage) message;
308             Invocation invoke = pendingInvocations.get(retMsg.invocationID);
309             if (invoke == null){
310                 logger.log(Level.WARNING, "Cannot find invocation ID: {0}", retMsg.invocationID);
311                 return;
312             }
313 
314             synchronized (invoke){
315                 invoke.retVal = retMsg.retVal;
316                 invoke.available = true;
317                 invoke.notifyAll();
318             }
319         }
320     }
321 
onConnection(HostedConnection conn)322     private void onConnection(HostedConnection conn) {
323         if (localObjects.size() > 0){
324             // send a object definition message
325             ObjectDef[] defs = new ObjectDef[localObjects.size()];
326             int i = 0;
327             for (Entry<LocalObject> entry : localObjects){
328                 defs[i] = makeObjectDef(entry.getValue());
329                 i++;
330             }
331 
332             RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
333             defMsg.objects = defs;
334             if (this.client != null){
335                 this.client.send(defMsg);
336                 logger.log(Level.INFO, "Client: Sending {0}", defMsg);
337             } else{
338                 conn.send(defMsg);
339                 logger.log(Level.INFO, "Server: Sending {0}", defMsg);
340             }
341         }
342     }
343 
344 }
345