Making classes final to make inheritance impossible
[iot2.git] / iotjava / iotrmi / Java / IoTRMIObject.java
index 9f40b821abc80f534cde557ecc6de45b3331fdc3..06a56ce5638a6e653411cffd373a00a6066da01e 100644 (file)
@@ -10,6 +10,9 @@ import java.util.Map;
 import java.util.Set;
 import java.lang.reflect.*;
 
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
 
 /** Class IoTRMIObject is a class that stores info of an object.
  *  <p>
@@ -23,143 +26,137 @@ import java.lang.reflect.*;
  * @version     1.0
  * @since       2016-10-03
  */
-public class IoTRMIObject {
+public final class IoTRMIObject {
 
        /**
         * Class Properties
         */
-       private Map<String,Method> mapSign2Method;      // Map from signature to method
-       private Map<Integer,String> mapHash2Sign;       // Map from hashcode(method ID) to signature
+       private List<String> listMethodId2Sign; // List of method signature (we use list index as method Id)
        private IoTRMIUtil rmiUtil;
        private IoTSocketServer rmiServer;
-       private Object obj;
-       private Class<?> cls;
-       private Method[] methods;
+       private byte[] methodBytes;
+       private Lock lock = new ReentrantLock();
 
 
        /**
         * Constructors
         */
-       public IoTRMIObject(String _clsName, int _port) throws 
-                       ClassNotFoundException, InstantiationException, 
-                               IllegalAccessException, IOException {
+       public IoTRMIObject(int _port) throws  
+               ClassNotFoundException, InstantiationException, 
+                       IllegalAccessException, IOException {
 
                rmiUtil = new IoTRMIUtil();
-               cls = Class.forName(_clsName);
-               obj = cls.newInstance();
-               methods = cls.getDeclaredMethods();
-               mapSign2Method = new HashMap<String,Method>();
-               mapHash2Sign = new HashMap<Integer,String>();
-               getMethodSignatures();  // Initialize the signature map
-               getMethodIds();                 // Initialize the method ID map
+               methodBytes = null;
                rmiServer = new IoTSocketServer(_port);
                rmiServer.connect();
        }
 
 
        /**
-        * getName() gets class name
+        * getMethodBytes() waits for method transmission in bytes
         */
-       public String getName() {
+       public byte[] getMethodBytes() throws IOException {
 
-               return cls.getName();
+               // Receive method info
+               //System.out.println("Method RMIObj before: " + Arrays.toString(methodBytes));
+               methodBytes = rmiServer.receiveBytes(methodBytes);
+               //System.out.println("Method RMIObj after: " + Arrays.toString(methodBytes));
+               return methodBytes;
        }
 
 
        /**
-        * getSignatures() gets method signatures
+        * getObjectId() gets object Id from bytes
         */
-       public Set<String> getSignatures() {
-
-               return mapSign2Method.keySet();
+       public int getObjectId() {
+
+               // Get object Id bytes
+               byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
+               System.arraycopy(methodBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
+               // Get object Id
+               int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
+               return objectId;
        }
 
-       
+
        /**
-        * getMethodParamTypes() gets method parameter types
+        * static version of getObjectId()
         */
-       public Class<?>[] getMethodParamTypes(String signature) {
-
-               Method method = mapSign2Method.get(signature);
-               return method.getParameterTypes();
+       public static int getObjectId(byte[] methodBytes) {
+
+               // Get object Id bytes
+               byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
+               System.arraycopy(methodBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
+               // Get object Id
+               int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
+               return objectId;
        }
 
 
        /**
-        * getMethodRetType() gets method return type
+        * setMethodBytes() sets bytes for method
         */
-       public Class<?> getMethodRetType(String signature) {
+       /*public void setMethodBytes(byte[] _methodBytes) throws IOException {
+
+               // Set method bytes
+               methodBytes = _methodBytes;
+       }*/
 
-               Method method = mapSign2Method.get(signature);
-               return method.getReturnType();
-       }
-       
 
        /**
-        * invokeMethod() invokes a method based on signature and params
+        * getMethodId() gets method Id from bytes
         */
-       public Object invokeMethod(String signature, Object[] params) {
-
-               Method method = mapSign2Method.get(signature);
-               Object retVal = null;
-               try {
-                       retVal = method.invoke(obj, params);
-               } catch (IllegalAccessException |
-                                InvocationTargetException ex) {
-                       ex.printStackTrace();
-                       throw new Error("IoTRMICall: Error invoking method: " + signature);
-               }
+       public int getMethodId() {
 
-               return retVal;
+               // Get method Id bytes
+               byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
+               // Method Id is positioned after object Id in the byte array
+               System.arraycopy(methodBytes, IoTRMIUtil.OBJECT_ID_LEN, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
+               // Get method Id
+               int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
+               // Get method Id
+               return methodId;
        }
 
 
        /**
-        * recvAndInvoke() waits for method transmission and invoke the method
+        * static version of getMethodId()
         */
-       private void recvAndInvoke() throws IOException {
+       public static int getMethodId(byte[] methodBytes) {
 
-               // Receive method info and invoke
-               byte[] method = null;
-               method = rmiServer.receiveBytes(method);
-               Object retObj = invokeMethod(method);
-               // Send back return value
-               byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
-               rmiServer.sendBytes(retObjBytes);
+               // Get method Id bytes
+               byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
+               // Method Id is positioned after object Id in the byte array
+               System.arraycopy(methodBytes, IoTRMIUtil.OBJECT_ID_LEN, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
+               // Get method Id
+               int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
+               // Get method Id
+               return methodId;
        }
-       
+
 
        /**
-        * invokeMethod() invokes a method based on byte array received
+        * getMethodParams() gets method params based on byte array received
         * <p>
         * Basically this is the format of a method in bytes:
-        * 1) 32-bit value of method ID (hash code)
-        * 2) m parameters with n-bit value each (m x n-bit)
+        * 1) 32-bit value of object ID
+        * 2) 32-bit value of method ID
+        * 3) m parameters with n-bit value each (m x n-bit)
         * For the parameters that don't have definite length,
         * we need to extract the length from a preceding 32-bit
         * field in front of it.
         *
         * For primitive objects:
-        * | 32-bit method ID | m-bit actual data (fixed length)  |
+        * | 32-bit object ID | 32-bit method ID | m-bit actual data (fixed length)  | ...
         * 
         * For string, arrays, and non-primitive objects:
-        * | 32-bit method ID | 32-bit length | n-bit actual data | ...
+        * | 32-bit object ID | 32-bit method ID | 32-bit length | n-bit actual data | ...
         * 
         */
-       public Object invokeMethod(byte[] methodBytes) {
+       public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenValCls) {
 
                // Byte scanning position
-               int pos = 0;
-               // Get method ID
-               byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
-               System.arraycopy(methodBytes, pos, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
-               pos = pos + IoTRMIUtil.METHOD_ID_LEN;
-               // Get Method object to handle method
-               int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
-               String signature = mapHash2Sign.get(methodId);
-               Method method = mapSign2Method.get(signature);
-               // Get class name and then param length
-               Class<?>[] arrCls = method.getParameterTypes();
+               int pos = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN;
                Object[] paramObj = new Object[arrCls.length];
                for (int i=0; i < arrCls.length; i++) {
 
@@ -176,56 +173,72 @@ public class IoTRMIObject {
                        byte[] paramBytes = new byte[paramSize];
                        System.arraycopy(methodBytes, pos, paramBytes, 0, paramSize);
                        pos = pos + paramSize;
-                       paramObj[i] = IoTRMIUtil.getParamObject(paramType, paramBytes);
-               }
-               Object retObj = null;
-               try {
-                       retObj = method.invoke(obj, paramObj);
-               } catch (IllegalAccessException |
-                                InvocationTargetException ex) {
-                       ex.printStackTrace();
-                       throw new Error("IoTRMICall: Error invoking method: " + signature);
+                       paramObj[i] = IoTRMIUtil.getParamObject(arrCls[i], arrGenValCls[i], paramBytes);
                }
 
-               return retObj;
+               return paramObj;
        }
 
 
-       /**================
-        * Helper methods
-        **================
-        */
        /**
-        * getMethodSignatures() gets methods signatures and store them in the Map
+        * sendReturnObj() sends back return Object to client
         */
-       private void getMethodSignatures() {
+       public void sendReturnObj(Object retObj) throws IOException {
 
-               for (Method m : methods) {
-                       String sign = rmiUtil.getSignature(m);
-                       //System.out.println("Signature: " + sign);
-                       mapSign2Method.put(sign, m);
-               }
+               // Send back return value
+               byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
+               rmiServer.sendBytes(retObjBytes);
        }
-       
-       
+
+
        /**
-        * getMethodIds() gets methods identifiers (hash code) and store them in the Map
+        * sendReturnObj() overloaded to send multiple return objects for structs
         */
-       private void getMethodIds() {
+       public void sendReturnObj(Class<?>[] retCls, Object[] retObj) throws IOException {
 
-               Set<String> setSignatures = getSignatures();
-               for (String sign : setSignatures) {
-                       byte[] hashCode = IoTRMIUtil.getHashCodeBytes(sign);
-                       int methodId = IoTRMIUtil.byteArrayToInt(hashCode);
-                       mapHash2Sign.put(methodId, sign);
-               }
+               // Send back return value
+               byte[] retObjBytes = returnToBytes(retCls, retObj);
+               rmiServer.sendBytes(retObjBytes);
        }
 
 
-       public static void main(String[] args) throws Exception {
+       /**
+        * returnToBytes() takes array of objects and generates bytes
+        */
+       public byte[] returnToBytes(Class<?>[] retCls, Object[] retObj) {
+
+               // Get byte arrays and calculate method bytes length
+               int numbRet = retObj.length;
+               int retLen = 0;
+               byte[][] objBytesArr = new byte[numbRet][];
+               for (int i = 0; i < numbRet; i++) {
+                       // Get byte arrays for the objects
+                       objBytesArr[i] = IoTRMIUtil.getObjectBytes(retObj[i]);
+                       String clsName = retCls[i].getSimpleName();
+                       int retObjLen = rmiUtil.getTypeSize(clsName);
+                       if (retObjLen == -1) {          // indefinite length - store the length first
+                               retLen = retLen + IoTRMIUtil.RETURN_LEN;
+                       }
+                       retLen = retLen + objBytesArr[i].length;
+               }
+               // Construct return in byte array
+               byte[] retBytes = new byte[retLen];
+               int pos = 0;
+               // Iteration for copying bytes
+               for (int i = 0; i < numbRet; i++) {
+
+                       String clsName = retCls[i].getSimpleName();
+                       int retObjLen = rmiUtil.getTypeSize(clsName);
+                       if (retObjLen == -1) {          // indefinite length
+                               retObjLen = objBytesArr[i].length;
+                               byte[] retLenBytes = IoTRMIUtil.intToByteArray(retObjLen);
+                               System.arraycopy(retLenBytes, 0, retBytes, pos, IoTRMIUtil.RETURN_LEN);
+                               pos = pos + IoTRMIUtil.RETURN_LEN;
+                       }               
+                       System.arraycopy(objBytesArr[i], 0, retBytes, pos, retObjLen);
+                       pos = pos + retObjLen;
+               }
 
-               int port = 5010;
-               IoTRMIObject rmiObj = new IoTRMIObject("TestClass", port);
-               rmiObj.recvAndInvoke();
+               return retBytes;
        }
 }