--- /dev/null
+package iotrmi.Java;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.lang.reflect.*;
+
+
+/** Class IoTRMIObject is a class that stores info of an object.
+ * <p>
+ * It stores object ID, methods, method ID, method's signature
+ * and parameters.
+ * This class also receive calls from different objects as they
+ * ask to execute certain methods remotely. This will have the
+ * execution result (return value) sent back to
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-10-03
+ */
+public 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 IoTRMIUtil rmiUtil;
+ private IoTSocketServer rmiServer;
+ private Object obj;
+ private Class<?> cls;
+ private Method[] methods;
+
+
+ /**
+ * Constructors
+ */
+ public IoTRMIObject(String _clsName, 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
+ rmiServer = new IoTSocketServer(_port);
+ rmiServer.connect();
+ }
+
+
+ /**
+ * getName() gets class name
+ */
+ public String getName() {
+
+ return cls.getName();
+ }
+
+
+ /**
+ * getSignatures() gets method signatures
+ */
+ public Set<String> getSignatures() {
+
+ return mapSign2Method.keySet();
+ }
+
+
+ /**
+ * getMethodParamTypes() gets method parameter types
+ */
+ public Class<?>[] getMethodParamTypes(String signature) {
+
+ Method method = mapSign2Method.get(signature);
+ return method.getParameterTypes();
+ }
+
+
+ /**
+ * getMethodRetType() gets method return type
+ */
+ public Class<?> getMethodRetType(String signature) {
+
+ Method method = mapSign2Method.get(signature);
+ return method.getReturnType();
+ }
+
+
+ /**
+ * invokeMethod() invokes a method based on signature and params
+ */
+ 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);
+ }
+
+ return retVal;
+ }
+
+
+ /**
+ * recvAndInvoke() waits for method transmission and invoke the method
+ */
+ private void recvAndInvoke() throws IOException {
+
+ // 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);
+ }
+
+
+ /**
+ * invokeMethod() invokes a method 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)
+ * 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) |
+ *
+ * For string, arrays, and non-primitive objects:
+ * | 32-bit method ID | 32-bit length | n-bit actual data | ...
+ *
+ */
+ public Object invokeMethod(byte[] methodBytes) {
+
+ // 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();
+ Object[] paramObj = new Object[arrCls.length];
+ for (int i=0; i < arrCls.length; i++) {
+
+ String paramType = arrCls[i].getSimpleName();
+ int paramSize = rmiUtil.getTypeSize(paramType);
+ // Get the 32-bit field in the byte array to get the actual
+ // length (this is a param with indefinite length)
+ if (paramSize == -1) {
+ byte[] bytPrmLen = new byte[IoTRMIUtil.PARAM_LEN];
+ System.arraycopy(methodBytes, pos, bytPrmLen, 0, IoTRMIUtil.PARAM_LEN);
+ pos = pos + IoTRMIUtil.PARAM_LEN;
+ paramSize = IoTRMIUtil.byteArrayToInt(bytPrmLen);
+ }
+ 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);
+ }
+
+ return retObj;
+ }
+
+
+ /**================
+ * Helper methods
+ **================
+ */
+ /**
+ * getMethodSignatures() gets methods signatures and store them in the Map
+ */
+ private void getMethodSignatures() {
+
+ for (Method m : methods) {
+ String sign = rmiUtil.getSignature(m);
+ //System.out.println("Signature: " + sign);
+ mapSign2Method.put(sign, m);
+ }
+ }
+
+
+ /**
+ * getMethodIds() gets methods identifiers (hash code) and store them in the Map
+ */
+ private void getMethodIds() {
+
+ Set<String> setSignatures = getSignatures();
+ for (String sign : setSignatures) {
+ byte[] hashCode = IoTRMIUtil.getHashCodeBytes(sign);
+ int methodId = IoTRMIUtil.byteArrayToInt(hashCode);
+ mapHash2Sign.put(methodId, sign);
+ }
+ }
+
+
+ public static void main(String[] args) throws Exception {
+
+ int port = 5010;
+ IoTRMIObject rmiObj = new IoTRMIObject("TestClass", port);
+ rmiObj.recvAndInvoke();
+ }
+}