Working Java v.1.0 for arbitrary calls of callback objects
[iot2.git] / iotjava / iotrmi / Java / IoTRMIObject.java
1 package iotrmi.Java;
2
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;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11 import java.lang.reflect.*;
12
13 import java.util.concurrent.*;
14 import java.util.concurrent.atomic.AtomicBoolean;
15
16
17 /** Class IoTRMIObject is a class that stores info of an object.
18  *  <p>
19  *  It stores object ID, methods, method ID, method's signature 
20  *  and parameters.
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 
24  *
25  * @author      Rahmadi Trimananda <rtrimana @ uci.edu>
26  * @version     1.0
27  * @since       2016-10-03
28  */
29 public class IoTRMIObject {
30
31         /**
32          * Class Properties
33          */
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;
41
42
43         /**
44          * Constructors
45          */
46         public IoTRMIObject(int _port) throws  
47                 ClassNotFoundException, InstantiationException, 
48                         IllegalAccessException, IOException {
49
50                 didGetMethodBytes = new AtomicBoolean(false);
51                 rmiUtil = new IoTRMIUtil();
52                 methodBytes = null;
53                 methodQueue = new ConcurrentLinkedQueue<byte[]>();
54                 mapSkeletonId = new HashMap<Integer,AtomicBoolean>();
55                 rmiServer = new IoTSocketServer(_port);
56                 rmiServer.connect();
57                 waitForMethod();
58                 wakeUpThread();
59         }
60
61
62         /**
63          * waitForMethod() starts a thread that waits for method bytes
64          */
65         public void waitForMethod() {
66
67                 Thread thread = new Thread() {
68                         public void run() {
69                                 byte[] methBytes = null;
70                                 while(true) {
71                                         try {
72                                                 methBytes = rmiServer.receiveBytes(methBytes);
73                                                 if (methBytes != null) {
74                                                         System.out.println("Command not null: " + Arrays.toString(methBytes));
75                                                         methodQueue.offer(methBytes);
76                                                 } else
77                                                         Thread.sleep(100);
78                                                 methBytes = null;
79                                         } catch (Exception ex) {
80                                                 ex.printStackTrace();
81                                                 throw new Error("IoTRMICall: Error receiving return value bytes!");
82                                         }
83                                 }
84                         }
85                 };
86                 thread.start();
87         }
88
89
90         /**
91          * wakeUpThread() wakes up the correct thread
92          */
93         public void wakeUpThread() {
94
95                 Thread thread = new Thread() {
96                         public void run() {
97                                 while(true) {
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
106                                         }
107                                 }
108                         }
109                 };
110                 thread.start();
111         }
112
113
114         /**
115          * registerSkeleton() registers the skeleton to be woken up
116          */
117         public synchronized void registerSkeleton(int objectId, AtomicBoolean methodReceived) {
118
119                 mapSkeletonId.put(objectId, methodReceived);
120         }
121
122
123         /**
124          * setGetMethodBytes() set didGetMethodBytes to true after getting the bytes
125          */
126         public boolean setGetMethodBytes() {
127
128                 return didGetMethodBytes.compareAndSet(false, true);
129         }
130
131
132         /**
133          * getMethodBytes() get the method in bytes
134          */
135         public byte[] getMethodBytes() throws IOException {
136
137                 // Just return the methodBytes content
138                 return methodBytes;
139         }
140
141
142         /**
143          * getObjectId() gets object Id from bytes
144          */
145         public int getObjectId() {
146
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);
150                 // Get object Id
151                 int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
152                 return objectId;
153         }
154
155
156         /**
157          * static version of getObjectId()
158          */
159         public static int getObjectId(byte[] methodBytes) {
160
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);
164                 // Get object Id
165                 int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
166                 return objectId;
167         }
168
169
170         /**
171          * getMethodId() gets method Id from bytes
172          */
173         public int getMethodId() {
174
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);
179                 // Get method Id
180                 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
181                 // Get method Id
182                 return methodId;
183         }
184
185
186         /**
187          * static version of getMethodId()
188          */
189         public static int getMethodId(byte[] methodBytes) {
190
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);
195                 // Get method Id
196                 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
197                 // Get method Id
198                 return methodId;
199         }
200
201
202         /**
203          * getMethodParams() gets method params based on byte array received
204          * <p>
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.
212          *
213          * For primitive objects:
214          * | 32-bit object ID | 32-bit method ID | m-bit actual data (fixed length)  | ...
215          * 
216          * For string, arrays, and non-primitive objects:
217          * | 32-bit object ID | 32-bit method ID | 32-bit length | n-bit actual data | ...
218          * 
219          */
220         public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenValCls) {
221
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++) {
226
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);
236                         }
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);
241                 }
242
243                 return paramObj;
244         }
245
246
247         /**
248          * getMethodParams() overloading
249          */
250         public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenValCls, byte[] methodBytes) {
251
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++) {
256
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);
266                         }
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);
271                 }
272
273                 return paramObj;
274         }
275
276
277         /**
278          * sendReturnObj() overloading
279          */
280         public void sendReturnObj(Object retObj) throws IOException {
281
282
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);
291         }
292
293
294         /**
295          * sendReturnObj() static version
296          */
297         public void sendReturnObj(Object retObj, byte[] methodBytes) throws IOException {
298
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);
308         }
309
310
311         /**
312          * sendReturnObj() overloaded to send multiple return objects for structs
313          */
314         public void sendReturnObj(Class<?>[] retCls, Object[] retObj) throws IOException {
315
316                 // Send back return value
317                 byte[] retObjBytes = returnToBytes(retCls, retObj);
318                 rmiServer.sendBytes(retObjBytes);
319         }
320
321
322         /**
323          * returnToBytes() takes array of objects and generates bytes
324          */
325         public byte[] returnToBytes(Class<?>[] retCls, Object[] retObj) {
326
327                 // Get byte arrays and calculate method bytes length
328                 int numbRet = retObj.length;
329                 int retLen = 0;
330                 byte[][] objBytesArr = new byte[numbRet][];
331                 for (int i = 0; i < numbRet; i++) {
332                         // Get byte arrays for the objects
333                         objBytesArr[i] = IoTRMIUtil.getObjectBytes(retObj[i]);
334                         String clsName = retCls[i].getSimpleName();
335                         int retObjLen = rmiUtil.getTypeSize(clsName);
336                         if (retObjLen == -1) {          // indefinite length - store the length first
337                                 retLen = retLen + IoTRMIUtil.RETURN_LEN;
338                         }
339                         retLen = retLen + objBytesArr[i].length;
340                 }
341                 // Construct return in byte array
342                 byte[] retBytes = new byte[retLen];
343                 int pos = 0;
344                 // Iteration for copying bytes
345                 for (int i = 0; i < numbRet; i++) {
346
347                         String clsName = retCls[i].getSimpleName();
348                         int retObjLen = rmiUtil.getTypeSize(clsName);
349                         if (retObjLen == -1) {          // indefinite length
350                                 retObjLen = objBytesArr[i].length;
351                                 byte[] retLenBytes = IoTRMIUtil.intToByteArray(retObjLen);
352                                 System.arraycopy(retLenBytes, 0, retBytes, pos, IoTRMIUtil.RETURN_LEN);
353                                 pos = pos + IoTRMIUtil.RETURN_LEN;
354                         }               
355                         System.arraycopy(objBytesArr[i], 0, retBytes, pos, retObjLen);
356                         pos = pos + retObjLen;
357                 }
358
359                 return retBytes;
360         }
361 }