Home | History | Annotate | Download | only in rmi
      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