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.serializing.Serializer;
36 import com.jme3.network.serializing.SerializerRegistration;
37 import java.io.IOException;
38 import java.lang.reflect.Method;
39 import java.nio.ByteBuffer;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
42 
43 /**
44  * {@link RmiSerializer} is responsible for serializing RMI messages
45  * like define object, call, and return.
46  *
47  * @author Kirill Vainer
48  */
49 public class RmiSerializer extends Serializer {
50 
51     private static final Logger logger = Logger.getLogger(RmiSerializer.class.getName());
52 
53     // not good for multithread applications
54     private char[] chrBuf = new char[256];
55 
writeString(ByteBuffer buffer, String string)56     private void writeString(ByteBuffer buffer, String string) throws IOException{
57         int length = string.length();
58         if (length > 255){
59             logger.log(Level.WARNING, "The string length exceeds the limit! {0} > 255", length);
60             buffer.put( (byte) 0 );
61             return;
62         }
63 
64         buffer.put( (byte) length );
65         for (int i = 0; i < length; i++){
66             buffer.put( (byte) string.charAt(i) );
67         }
68     }
69 
readString(ByteBuffer buffer)70     private String readString(ByteBuffer buffer){
71         int length = buffer.get() & 0xff;
72         for (int i = 0; i < length; i++){
73             chrBuf[i] = (char) (buffer.get() & 0xff);
74         }
75         return String.valueOf(chrBuf, 0, length);
76     }
77 
writeType(ByteBuffer buffer, Class<?> clazz)78     private void writeType(ByteBuffer buffer, Class<?> clazz) throws IOException{
79         if (clazz == void.class){
80             buffer.putShort((short)0);
81         } else {
82             SerializerRegistration reg = Serializer.getSerializerRegistration(clazz);
83             if (reg == null){
84                 logger.log(Level.WARNING, "Unknown class: {0}", clazz);
85                 throw new IOException(); // prevents message from being serialized
86             }
87             buffer.putShort(reg.getId());
88         }
89     }
90 
readType(ByteBuffer buffer)91     private Class<?> readType(ByteBuffer buffer) throws IOException{
92         SerializerRegistration reg = Serializer.readClass(buffer);
93         if (reg == null){
94             // either "void" or unknown val
95             short id = buffer.getShort(buffer.position()-2);
96             if (id == 0){
97                 return void.class;
98             } else{
99                 logger.log(Level.WARNING, "Undefined class ID: {0}", id);
100                 throw new IOException(); // prevents message from being serialized
101             }
102         }
103         return reg.getType();
104     }
105 
writeMethod(ByteBuffer buffer, Method method)106     private void writeMethod(ByteBuffer buffer, Method method) throws IOException{
107         String     name = method.getName();
108         Class<?>[] paramTypes = method.getParameterTypes();
109         Class<?>   returnType = method.getReturnType();
110 
111         writeString(buffer, name);
112         writeType(buffer, returnType);
113         buffer.put((byte)paramTypes.length);
114         for (Class<?> paramType : paramTypes)
115             writeType(buffer, paramType);
116     }
117 
readMethod(ByteBuffer buffer)118     private MethodDef readMethod(ByteBuffer buffer) throws IOException{
119         String name = readString(buffer);
120         Class<?> retType = readType(buffer);
121 
122         int numParams = buffer.get() & 0xff;
123         Class<?>[] paramTypes = new Class<?>[numParams];
124         for (int i = 0; i < numParams; i++){
125             paramTypes[i] = readType(buffer);
126         }
127 
128         MethodDef def = new MethodDef();
129         def.name = name;
130         def.paramTypes = paramTypes;
131         def.retType = retType;
132         return def;
133     }
134 
writeObjectDef(ByteBuffer buffer, ObjectDef def)135     private void writeObjectDef(ByteBuffer buffer, ObjectDef def) throws IOException{
136         buffer.putShort((short)def.objectId);
137         writeString(buffer, def.objectName);
138         Method[] methods = def.methods;
139         buffer.put( (byte) methods.length );
140         for (Method method : methods){
141             writeMethod(buffer, method);
142         }
143     }
144 
readObjectDef(ByteBuffer buffer)145     private ObjectDef readObjectDef(ByteBuffer buffer) throws IOException{
146         ObjectDef def = new ObjectDef();
147 
148         def.objectId = buffer.getShort();
149         def.objectName = readString(buffer);
150 
151         int numMethods = buffer.get() & 0xff;
152         MethodDef[] methodDefs = new MethodDef[numMethods];
153         for (int i = 0; i < numMethods; i++){
154             methodDefs[i] = readMethod(buffer);
155         }
156         def.methodDefs = methodDefs;
157         return def;
158     }
159 
writeObjectDefs(ByteBuffer buffer, RemoteObjectDefMessage defMsg)160     private void writeObjectDefs(ByteBuffer buffer, RemoteObjectDefMessage defMsg) throws IOException{
161         ObjectDef[] defs = defMsg.objects;
162         buffer.put( (byte) defs.length );
163         for (ObjectDef def : defs)
164             writeObjectDef(buffer, def);
165     }
166 
readObjectDefs(ByteBuffer buffer)167     private RemoteObjectDefMessage readObjectDefs(ByteBuffer buffer) throws IOException{
168         RemoteObjectDefMessage defMsg = new RemoteObjectDefMessage();
169         int numObjs = buffer.get() & 0xff;
170         ObjectDef[] defs = new ObjectDef[numObjs];
171         for (int i = 0; i < numObjs; i++){
172             defs[i] = readObjectDef(buffer);
173         }
174         defMsg.objects = defs;
175         return defMsg;
176     }
177 
writeMethodCall(ByteBuffer buffer, RemoteMethodCallMessage call)178     private void writeMethodCall(ByteBuffer buffer, RemoteMethodCallMessage call) throws IOException{
179         buffer.putShort((short)call.objectId);
180         buffer.putShort(call.methodId);
181         buffer.putShort(call.invocationId);
182         if (call.args == null){
183             buffer.put((byte)0);
184         }else{
185             buffer.put((byte)call.args.length);
186 
187             // Right now it writes 0 for every null argument
188             // and 1 for every non-null argument followed by the serialized
189             // argument. For the future, using a bit set should be considered.
190             for (Object obj : call.args){
191                 if (obj != null){
192                     buffer.put((byte)0x01);
193                     Serializer.writeClassAndObject(buffer, obj);
194                 }else{
195                     buffer.put((byte)0x00);
196                 }
197             }
198         }
199     }
200 
readMethodCall(ByteBuffer buffer)201     private RemoteMethodCallMessage readMethodCall(ByteBuffer buffer) throws IOException{
202         RemoteMethodCallMessage call = new RemoteMethodCallMessage();
203         call.objectId = buffer.getShort();
204         call.methodId = buffer.getShort();
205         call.invocationId = buffer.getShort();
206         int numArgs = buffer.get() & 0xff;
207         if (numArgs > 0){
208             Object[] args = new Object[numArgs];
209             for (int i = 0; i < numArgs; i++){
210                 if (buffer.get() == (byte)0x01){
211                     args[i] = Serializer.readClassAndObject(buffer);
212                 }
213             }
214             call.args = args;
215         }
216         return call;
217     }
218 
writeMethodReturn(ByteBuffer buffer, RemoteMethodReturnMessage ret)219     private void writeMethodReturn(ByteBuffer buffer, RemoteMethodReturnMessage ret) throws IOException{
220         buffer.putShort(ret.invocationID);
221         if (ret.retVal != null){
222             buffer.put((byte)0x01);
223             Serializer.writeClassAndObject(buffer, ret.retVal);
224         }else{
225             buffer.put((byte)0x00);
226         }
227     }
228 
readMethodReturn(ByteBuffer buffer)229     private RemoteMethodReturnMessage readMethodReturn(ByteBuffer buffer) throws IOException{
230         RemoteMethodReturnMessage ret = new RemoteMethodReturnMessage();
231         ret.invocationID = buffer.getShort();
232         if (buffer.get() == (byte)0x01){
233             ret.retVal = Serializer.readClassAndObject(buffer);
234         }
235         return ret;
236     }
237 
238     @Override
readObject(ByteBuffer data, Class<T> c)239     public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
240         if (c == RemoteObjectDefMessage.class){
241             return (T) readObjectDefs(data);
242         }else if (c == RemoteMethodCallMessage.class){
243             return (T) readMethodCall(data);
244         }else if (c == RemoteMethodReturnMessage.class){
245             return (T) readMethodReturn(data);
246         }
247         return null;
248     }
249 
250     @Override
writeObject(ByteBuffer buffer, Object object)251     public void writeObject(ByteBuffer buffer, Object object) throws IOException {
252 //        int p = buffer.position();
253         if (object instanceof RemoteObjectDefMessage){
254             RemoteObjectDefMessage def = (RemoteObjectDefMessage) object;
255             writeObjectDefs(buffer, def);
256         }else if (object instanceof RemoteMethodCallMessage){
257             RemoteMethodCallMessage call = (RemoteMethodCallMessage) object;
258             writeMethodCall(buffer, call);
259         }else if (object instanceof RemoteMethodReturnMessage){
260             RemoteMethodReturnMessage ret = (RemoteMethodReturnMessage) object;
261             writeMethodReturn(buffer, ret);
262         }
263 //        p = buffer.position() - p;
264 //        System.out.println(object+": uses " + p + " bytes");
265     }
266 
267 }
268