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.*;
13 import java.util.concurrent.*;
14 import java.util.concurrent.atomic.AtomicBoolean;
17 /** Class IoTRMIObject is a class that stores info of an object.
19 * It stores object ID, methods, method ID, method's signature
21 * This class also receive calls from different objects as they
22 * ask to execute certain methods remotely. This will have the
23 * execution result (return value) sent back to
25 * @author Rahmadi Trimananda <rtrimana @ uci.edu>
29 public class IoTRMIObject {
34 private List<String> listMethodId2Sign; // List of method signature (we use list index as method Id)
35 private IoTRMIUtil rmiUtil;
36 private IoTSocketServer rmiServer;
37 private byte[] methodBytes;
38 private ConcurrentLinkedQueue<byte[]> methodQueue;
39 private Map<Integer,AtomicBoolean> mapSkeletonId;
40 private AtomicBoolean didGetMethodBytes;
46 public IoTRMIObject(int _port) throws
47 ClassNotFoundException, InstantiationException,
48 IllegalAccessException, IOException {
50 didGetMethodBytes = new AtomicBoolean(false);
51 rmiUtil = new IoTRMIUtil();
53 methodQueue = new ConcurrentLinkedQueue<byte[]>();
54 mapSkeletonId = new HashMap<Integer,AtomicBoolean>();
55 rmiServer = new IoTSocketServer(_port);
63 * waitForMethod() starts a thread that waits for method bytes
65 public void waitForMethod() {
67 Thread thread = new Thread() {
69 byte[] methBytes = null;
72 methBytes = rmiServer.receiveBytes(methBytes);
73 if (methBytes != null) {
74 System.out.println("Command not null: " + Arrays.toString(methBytes));
75 methodQueue.offer(methBytes);
79 } catch (Exception ex) {
81 throw new Error("IoTRMICall: Error receiving return value bytes!");
91 * wakeUpThread() wakes up the correct thread
93 public void wakeUpThread() {
95 Thread thread = new Thread() {
98 // Take the current method from the queue and wake up the correct thread
99 methodBytes = methodQueue.poll();
100 if (methodBytes != null) { // If there is method bytes
101 int currObjId = getObjectId(methodBytes);
102 AtomicBoolean methRecv = mapSkeletonId.get(currObjId);
103 didGetMethodBytes.set(false);
104 while(!methRecv.compareAndSet(false, true));
105 while(!didGetMethodBytes.get()); // While skeleton is still processing
115 * registerSkeleton() registers the skeleton to be woken up
117 public synchronized void registerSkeleton(int objectId, AtomicBoolean methodReceived) {
119 mapSkeletonId.put(objectId, methodReceived);
124 * setGetMethodBytes() set didGetMethodBytes to true after getting the bytes
126 public boolean setGetMethodBytes() {
128 return didGetMethodBytes.compareAndSet(false, true);
133 * getMethodBytes() get the method in bytes
135 public byte[] getMethodBytes() throws IOException {
137 // Just return the methodBytes content
143 * getObjectId() gets object Id from bytes
145 public int getObjectId() {
147 // Get object Id bytes
148 byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
149 System.arraycopy(methodBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
151 int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
157 * static version of getObjectId()
159 public static int getObjectId(byte[] methodBytes) {
161 // Get object Id bytes
162 byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
163 System.arraycopy(methodBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
165 int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
171 * getMethodId() gets method Id from bytes
173 public int getMethodId() {
175 // Get method Id bytes
176 byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
177 // Method Id is positioned after object Id in the byte array
178 System.arraycopy(methodBytes, IoTRMIUtil.OBJECT_ID_LEN, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
180 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
187 * static version of getMethodId()
189 public static int getMethodId(byte[] methodBytes) {
191 // Get method Id bytes
192 byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
193 // Method Id is positioned after object Id in the byte array
194 System.arraycopy(methodBytes, IoTRMIUtil.OBJECT_ID_LEN, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
196 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
203 * getMethodParams() gets method params based on byte array received
205 * Basically this is the format of a method in bytes:
206 * 1) 32-bit value of object ID
207 * 2) 32-bit value of method ID
208 * 3) m parameters with n-bit value each (m x n-bit)
209 * For the parameters that don't have definite length,
210 * we need to extract the length from a preceding 32-bit
211 * field in front of it.
213 * For primitive objects:
214 * | 32-bit object ID | 32-bit method ID | m-bit actual data (fixed length) | ...
216 * For string, arrays, and non-primitive objects:
217 * | 32-bit object ID | 32-bit method ID | 32-bit length | n-bit actual data | ...
220 public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenValCls) {
222 // Byte scanning position
223 int pos = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN;
224 Object[] paramObj = new Object[arrCls.length];
225 for (int i=0; i < arrCls.length; i++) {
227 String paramType = arrCls[i].getSimpleName();
228 int paramSize = rmiUtil.getTypeSize(paramType);
229 // Get the 32-bit field in the byte array to get the actual
230 // length (this is a param with indefinite length)
231 if (paramSize == -1) {
232 byte[] bytPrmLen = new byte[IoTRMIUtil.PARAM_LEN];
233 System.arraycopy(methodBytes, pos, bytPrmLen, 0, IoTRMIUtil.PARAM_LEN);
234 pos = pos + IoTRMIUtil.PARAM_LEN;
235 paramSize = IoTRMIUtil.byteArrayToInt(bytPrmLen);
237 byte[] paramBytes = new byte[paramSize];
238 System.arraycopy(methodBytes, pos, paramBytes, 0, paramSize);
239 pos = pos + paramSize;
240 paramObj[i] = IoTRMIUtil.getParamObject(arrCls[i], arrGenValCls[i], paramBytes);
248 * getMethodParams() overloading
250 public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenValCls, byte[] methodBytes) {
252 // Byte scanning position
253 int pos = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN;
254 Object[] paramObj = new Object[arrCls.length];
255 for (int i=0; i < arrCls.length; i++) {
257 String paramType = arrCls[i].getSimpleName();
258 int paramSize = rmiUtil.getTypeSize(paramType);
259 // Get the 32-bit field in the byte array to get the actual
260 // length (this is a param with indefinite length)
261 if (paramSize == -1) {
262 byte[] bytPrmLen = new byte[IoTRMIUtil.PARAM_LEN];
263 System.arraycopy(methodBytes, pos, bytPrmLen, 0, IoTRMIUtil.PARAM_LEN);
264 pos = pos + IoTRMIUtil.PARAM_LEN;
265 paramSize = IoTRMIUtil.byteArrayToInt(bytPrmLen);
267 byte[] paramBytes = new byte[paramSize];
268 System.arraycopy(methodBytes, pos, paramBytes, 0, paramSize);
269 pos = pos + paramSize;
270 paramObj[i] = IoTRMIUtil.getParamObject(arrCls[i], arrGenValCls[i], paramBytes);
278 * sendReturnObj() overloading
280 public void sendReturnObj(Object retObj) throws IOException {
283 byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
284 // Send return value together with OBJECT_ID and METHOD_ID for arbitration
285 byte[] retAllBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN + retObjBytes.length];
286 // Copy OBJECT_ID and METHOD_ID
287 System.arraycopy(methodBytes, 0, retAllBytes, 0, IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN);
288 // Copy array of bytes (return object)
289 System.arraycopy(retObjBytes, 0, retAllBytes, IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN, retObjBytes.length);
290 rmiServer.sendBytes(retAllBytes);
295 * sendReturnObj() static version
297 public void sendReturnObj(Object retObj, byte[] methodBytes) throws IOException {
299 // Send back return value
300 byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
301 // Send return value together with OBJECT_ID and METHOD_ID for arbitration
302 byte[] retAllBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN + retObjBytes.length];
303 // Copy OBJECT_ID and METHOD_ID
304 System.arraycopy(methodBytes, 0, retAllBytes, 0, IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN);
305 // Copy array of bytes (return object)
306 System.arraycopy(retObjBytes, 0, retAllBytes, IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN, retObjBytes.length);
307 rmiServer.sendBytes(retAllBytes);
312 * sendReturnObj() overloaded to send multiple return objects for structs
314 public void sendReturnObj(Class<?>[] retCls, Object[] retObj, byte[] methodBytes) throws IOException {
316 // Send back return value
317 byte[] retObjBytes = returnToBytes(retCls, retObj);
318 // Send return value together with OBJECT_ID and METHOD_ID for arbitration
319 byte[] retAllBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN + retObjBytes.length];
320 // Copy OBJECT_ID and METHOD_ID
321 System.arraycopy(methodBytes, 0, retAllBytes, 0, IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN);
322 // Copy array of bytes (return object)
323 rmiServer.sendBytes(retAllBytes);
328 * returnToBytes() takes array of objects and generates bytes
330 public byte[] returnToBytes(Class<?>[] retCls, Object[] retObj) {
332 // Get byte arrays and calculate method bytes length
333 int numbRet = retObj.length;
335 byte[][] objBytesArr = new byte[numbRet][];
336 for (int i = 0; i < numbRet; i++) {
337 // Get byte arrays for the objects
338 objBytesArr[i] = IoTRMIUtil.getObjectBytes(retObj[i]);
339 String clsName = retCls[i].getSimpleName();
340 int retObjLen = rmiUtil.getTypeSize(clsName);
341 if (retObjLen == -1) { // indefinite length - store the length first
342 retLen = retLen + IoTRMIUtil.RETURN_LEN;
344 retLen = retLen + objBytesArr[i].length;
346 // Construct return in byte array
347 byte[] retBytes = new byte[retLen];
349 // Iteration for copying bytes
350 for (int i = 0; i < numbRet; i++) {
352 String clsName = retCls[i].getSimpleName();
353 int retObjLen = rmiUtil.getTypeSize(clsName);
354 if (retObjLen == -1) { // indefinite length
355 retObjLen = objBytesArr[i].length;
356 byte[] retLenBytes = IoTRMIUtil.intToByteArray(retObjLen);
357 System.arraycopy(retLenBytes, 0, retBytes, pos, IoTRMIUtil.RETURN_LEN);
358 pos = pos + IoTRMIUtil.RETURN_LEN;
360 System.arraycopy(objBytesArr[i], 0, retBytes, pos, retObjLen);
361 pos = pos + retObjLen;