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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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