3 import java.io.IOException;
4 import java.nio.ByteBuffer;
5 import java.util.Arrays;
6 import java.util.ArrayList;
7 import java.util.HashMap;
11 import java.lang.reflect.*;
14 /** Class IoTRMIObject is a class that stores info of an object.
16 * It stores object ID, methods, method ID, method's signature
18 * This class also receive calls from different objects as they
19 * ask to execute certain methods remotely. This will have the
20 * execution result (return value) sent back to
22 * @author Rahmadi Trimananda <rtrimana @ uci.edu>
26 public class IoTRMIObject {
31 private Map<String,Method> mapSign2Method; // Map from signature to method
32 private Map<Integer,String> mapHash2Sign; // Map from hashcode(method ID) to signature
33 private IoTRMIUtil rmiUtil;
34 private IoTSocketServer rmiServer;
37 private Method[] methods;
43 public IoTRMIObject(String _clsName, int _port) throws
44 ClassNotFoundException, InstantiationException,
45 IllegalAccessException, IOException {
47 rmiUtil = new IoTRMIUtil();
48 cls = Class.forName(_clsName);
49 obj = cls.newInstance();
50 methods = cls.getDeclaredMethods();
51 mapSign2Method = new HashMap<String,Method>();
52 mapHash2Sign = new HashMap<Integer,String>();
53 getMethodSignatures(); // Initialize the signature map
54 getMethodIds(); // Initialize the method ID map
55 rmiServer = new IoTSocketServer(_port);
61 * getName() gets class name
63 public String getName() {
70 * getSignatures() gets method signatures
72 public Set<String> getSignatures() {
74 return mapSign2Method.keySet();
79 * getMethodParamTypes() gets method parameter types
81 public Class<?>[] getMethodParamTypes(String signature) {
83 Method method = mapSign2Method.get(signature);
84 return method.getParameterTypes();
89 * getMethodRetType() gets method return type
91 public Class<?> getMethodRetType(String signature) {
93 Method method = mapSign2Method.get(signature);
94 return method.getReturnType();
99 * invokeMethod() invokes a method based on signature and params
101 public Object invokeMethod(String signature, Object[] params) {
103 Method method = mapSign2Method.get(signature);
104 Object retVal = null;
106 retVal = method.invoke(obj, params);
107 } catch (IllegalAccessException |
108 InvocationTargetException ex) {
109 ex.printStackTrace();
110 throw new Error("IoTRMICall: Error invoking method: " + signature);
118 * recvAndInvoke() waits for method transmission and invoke the method
120 private void recvAndInvoke() throws IOException {
122 // Receive method info and invoke
123 byte[] method = null;
124 method = rmiServer.receiveBytes(method);
125 Object retObj = invokeMethod(method);
126 // Send back return value
127 byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
128 rmiServer.sendBytes(retObjBytes);
133 * invokeMethod() invokes a method based on byte array received
135 * Basically this is the format of a method in bytes:
136 * 1) 32-bit value of method ID (hash code)
137 * 2) m parameters with n-bit value each (m x n-bit)
138 * For the parameters that don't have definite length,
139 * we need to extract the length from a preceding 32-bit
140 * field in front of it.
142 * For primitive objects:
143 * | 32-bit method ID | m-bit actual data (fixed length) |
145 * For string, arrays, and non-primitive objects:
146 * | 32-bit method ID | 32-bit length | n-bit actual data | ...
149 public Object invokeMethod(byte[] methodBytes) {
151 // Byte scanning position
154 byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
155 System.arraycopy(methodBytes, pos, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
156 pos = pos + IoTRMIUtil.METHOD_ID_LEN;
157 // Get Method object to handle method
158 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
159 String signature = mapHash2Sign.get(methodId);
160 Method method = mapSign2Method.get(signature);
161 // Get class name and then param length
162 Class<?>[] arrCls = method.getParameterTypes();
163 Object[] paramObj = new Object[arrCls.length];
164 for (int i=0; i < arrCls.length; i++) {
166 String paramType = arrCls[i].getSimpleName();
167 int paramSize = rmiUtil.getTypeSize(paramType);
168 // Get the 32-bit field in the byte array to get the actual
169 // length (this is a param with indefinite length)
170 if (paramSize == -1) {
171 byte[] bytPrmLen = new byte[IoTRMIUtil.PARAM_LEN];
172 System.arraycopy(methodBytes, pos, bytPrmLen, 0, IoTRMIUtil.PARAM_LEN);
173 pos = pos + IoTRMIUtil.PARAM_LEN;
174 paramSize = IoTRMIUtil.byteArrayToInt(bytPrmLen);
176 byte[] paramBytes = new byte[paramSize];
177 System.arraycopy(methodBytes, pos, paramBytes, 0, paramSize);
178 pos = pos + paramSize;
179 paramObj[i] = IoTRMIUtil.getParamObject(paramType, paramBytes);
181 Object retObj = null;
183 retObj = method.invoke(obj, paramObj);
184 } catch (IllegalAccessException |
185 InvocationTargetException ex) {
186 ex.printStackTrace();
187 throw new Error("IoTRMICall: Error invoking method: " + signature);
199 * getMethodSignatures() gets methods signatures and store them in the Map
201 private void getMethodSignatures() {
203 for (Method m : methods) {
204 String sign = rmiUtil.getSignature(m);
205 //System.out.println("Signature: " + sign);
206 mapSign2Method.put(sign, m);
212 * getMethodIds() gets methods identifiers (hash code) and store them in the Map
214 private void getMethodIds() {
216 Set<String> setSignatures = getSignatures();
217 for (String sign : setSignatures) {
218 byte[] hashCode = IoTRMIUtil.getHashCodeBytes(sign);
219 int methodId = IoTRMIUtil.byteArrayToInt(hashCode);
220 mapHash2Sign.put(methodId, sign);
225 public static void main(String[] args) throws Exception {
228 IoTRMIObject rmiObj = new IoTRMIObject("TestClass", port);
229 rmiObj.recvAndInvoke();