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>
* @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++) {
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;
}
}