Adding config file for sharing.
[iot2.git] / iotjava / iotrmi / Java / IoTRMIComm.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 /** Abstract class IoTRMIComm is a class that combines IoTRMIObject and IoTRMICall
18  *  <p>
19  *  We will arbitrate packets into 2 queues and wake up the right threads/callers.
20  *  We separate traffics one-directionally.
21  *
22  * @author      Rahmadi Trimananda <rtrimana @ uci.edu>
23  * @version     1.0
24  * @since       2017-01-27
25  */
26 public abstract class IoTRMIComm {
27
28         /**
29          * Class Properties
30          */
31         protected IoTRMIUtil rmiUtil;
32         protected byte[] methodBytes;
33         protected byte[] retValueBytes;
34         protected ConcurrentLinkedQueue<byte[]> methodQueue;
35         protected ConcurrentLinkedQueue<byte[]> returnQueue;
36         protected Map<Integer,AtomicBoolean> mapSkeletonId;
37         protected Map<String,AtomicBoolean> mapStubId;
38         protected AtomicBoolean didGetMethodBytes;
39         protected AtomicBoolean didGetReturnBytes;
40         protected int objectIdCounter = Integer.MAX_VALUE;
41
42         /**
43          * Constructor (for skeleton)
44          */
45         public IoTRMIComm() throws  
46                 ClassNotFoundException, InstantiationException, 
47                         IllegalAccessException, IOException {
48
49                 rmiUtil = new IoTRMIUtil();
50                 methodBytes = null;
51                 retValueBytes = null;
52                 methodQueue = new ConcurrentLinkedQueue<byte[]>();
53                 returnQueue = new ConcurrentLinkedQueue<byte[]>();
54                 mapSkeletonId = new HashMap<Integer,AtomicBoolean>();
55                 mapStubId = new HashMap<String,AtomicBoolean>();
56                 didGetMethodBytes = new AtomicBoolean(false);
57                 didGetReturnBytes = new AtomicBoolean(false);
58                 wakeUpThreadOnMethodCall();
59                 wakeUpThreadOnReturnValue();
60         }
61
62
63         /**
64          * wakeUpThreadOnMethodCall() wakes up the correct thread when receiving method call
65          */
66         private void wakeUpThreadOnMethodCall() {
67
68                 Thread thread = new Thread() {
69                         public void run() {
70                                 while(true) {
71                                         // Take the current method from the queue and wake up the correct thread
72                                         methodBytes = methodQueue.poll();
73                                         if (methodBytes != null) {      // If there is method bytes
74                                                 int currObjId = getObjectId(methodBytes);
75                                                 AtomicBoolean methRecv = mapSkeletonId.get(currObjId);
76                                                 didGetMethodBytes.set(false);
77                                                 while(!methRecv.compareAndSet(false, true));
78                                                 while(!didGetMethodBytes.get());        // While skeleton is still processing
79                                         }
80                                 }
81                         }
82                 };
83                 thread.start();
84         }
85
86
87         /**
88          * wakeUpThreadOnReturnValue() wakes up the correct thread when receiving return value
89          */
90         private void wakeUpThreadOnReturnValue() {
91
92                 Thread thread = new Thread() {
93                         public void run() {
94                                 while(true) {
95                                         // Take the current method from the queue and wake up the correct thread
96                                         retValueBytes = returnQueue.poll();
97                                         if (retValueBytes != null) {    // If there is method bytes
98                                                 int objectId = getObjectId(retValueBytes);
99                                                 int methodId = getMethodId(retValueBytes);
100                                                 String strKey = objectId + "-" + methodId;
101                                                 AtomicBoolean retRecv = mapStubId.get(strKey);
102                                                 didGetReturnBytes.set(false);
103                                                 while(!retRecv.compareAndSet(false, true));
104                                                 while(!didGetReturnBytes.get());        // While skeleton is still processing
105                                         }
106                                 }
107                         }
108                 };
109                 thread.start();
110         }
111
112
113         /**
114          * registerSkeleton() registers the skeleton to be woken up
115          */
116         public synchronized void registerSkeleton(int objectId, AtomicBoolean methodReceived) {
117
118                 mapSkeletonId.put(objectId, methodReceived);
119         }
120
121
122         /**
123          * registerStub() registers the skeleton to be woken up
124          */
125         public synchronized void registerStub(int objectId, int methodId, AtomicBoolean retValueReceived) {
126
127                 String strKey = objectId + "-" + methodId;
128                 mapStubId.put(strKey, retValueReceived);
129         }
130
131
132         /**
133          * getObjectIdCounter() gets object Id counter
134          */
135         public int getObjectIdCounter() {
136
137                 return objectIdCounter;
138         }
139
140
141         /**
142          * setObjectIdCounter() sets object Id counter
143          */
144         public void setObjectIdCounter(int objIdCounter) {
145
146                 objectIdCounter = objIdCounter;
147         }
148
149
150         /**
151          * decrementObjectIdCounter() gets object Id counter
152          */
153         public void decrementObjectIdCounter() {
154
155                 objectIdCounter--;
156         }
157
158
159         /**
160          * setGetMethodBytes() set didGetMethodBytes to true after getting the bytes
161          */
162         public boolean setGetMethodBytes() {
163
164                 return didGetMethodBytes.compareAndSet(false, true);
165         }
166
167
168         /**
169          * setGetReturnBytes() set didGetReturnBytes if there is a new return value already
170          */
171         public synchronized boolean setGetReturnBytes() {
172
173                 return didGetReturnBytes.compareAndSet(false, true);
174         }
175
176
177         /**
178          * getMethodBytes() get the method in bytes
179          */
180         public byte[] getMethodBytes() throws IOException {
181
182                 // Just return the methodBytes content
183                 return methodBytes;
184         }
185
186
187         /**
188          * static version of getObjectId()
189          */
190         public static int getObjectId(byte[] packetBytes) {
191
192                 // Get object Id bytes
193                 byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
194                 System.arraycopy(packetBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
195                 // Get object Id
196                 int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
197                 return objectId;
198         }
199
200
201         /**
202          * static version of getMethodId()
203          */
204         public static int getMethodId(byte[] packetBytes) {
205
206                 // Get method Id bytes
207                 byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
208                 // Method Id is positioned after object Id in the byte array
209                 System.arraycopy(packetBytes, IoTRMIUtil.OBJECT_ID_LEN, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
210                 // Get method Id
211                 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
212                 return methodId;
213         }
214
215
216         /**
217          * static version of getPacketType() - either method or return value (position is after object Id and method Id)
218          */
219         public static int getPacketType(byte[] packetBytes) {
220
221                 // Get packet type bytes
222                 byte[] packetTypeBytes = new byte[IoTRMIUtil.PACKET_TYPE_LEN];
223                 int offset = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN;
224                 System.arraycopy(packetBytes, offset, packetTypeBytes, 0, IoTRMIUtil.PACKET_TYPE_LEN);
225                 // Get packet type (for now we assume 1 as method and -1 as return value
226                 int packetType = IoTRMIUtil.byteArrayToInt(packetTypeBytes);
227                 return packetType;
228         }
229
230
231         /**
232          * getMethodParams() gets method params based on byte array received
233          * <p>
234          * Basically this is the format of a method in bytes:
235          * 1) 32-bit value of object ID
236          * 2) 32-bit value of method ID
237          * 3) m parameters with n-bit value each (m x n-bit)
238          * For the parameters that don't have definite length,
239          * we need to extract the length from a preceding 32-bit
240          * field in front of it.
241          *
242          * For primitive objects:
243          * | 32-bit object ID | 32-bit method ID | m-bit actual data (fixed length)  | ...
244          * 
245          * For string, arrays, and non-primitive objects:
246          * | 32-bit object ID | 32-bit method ID | 32-bit length | n-bit actual data | ...
247          * 
248          */
249         public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenValCls, byte[] methodBytes) {
250
251                 // Byte scanning position
252                 int pos = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN + IoTRMIUtil.PACKET_TYPE_LEN;
253                 Object[] paramObj = new Object[arrCls.length];
254                 for (int i=0; i < arrCls.length; i++) {
255
256                         String paramType = arrCls[i].getSimpleName();
257                         int paramSize = rmiUtil.getTypeSize(paramType);
258                         // Get the 32-bit field in the byte array to get the actual
259                         //              length (this is a param with indefinite length)
260                         if (paramSize == -1) {
261                                 byte[] bytPrmLen = new byte[IoTRMIUtil.PARAM_LEN];
262                                 System.arraycopy(methodBytes, pos, bytPrmLen, 0, IoTRMIUtil.PARAM_LEN);
263                                 pos = pos + IoTRMIUtil.PARAM_LEN;
264                                 paramSize = IoTRMIUtil.byteArrayToInt(bytPrmLen);
265                         }
266                         byte[] paramBytes = new byte[paramSize];
267                         System.arraycopy(methodBytes, pos, paramBytes, 0, paramSize);
268                         pos = pos + paramSize;
269                         paramObj[i] = IoTRMIUtil.getParamObject(arrCls[i], arrGenValCls[i], paramBytes);
270                 }
271
272                 return paramObj;
273         }
274
275
276         /**
277          * sendReturnObj() abstract version
278          */
279         public abstract void sendReturnObj(Object retObj, byte[] methodBytes);
280
281
282         /**
283          * sendReturnObj() abstract version
284          */
285         public abstract void sendReturnObj(Class<?>[] retCls, Object[] retObj, byte[] methodBytes);
286
287
288         /**
289          * returnToBytes() takes array of objects and generates bytes
290          */
291         public byte[] returnToBytes(Class<?>[] retCls, Object[] retObj) {
292
293                 // Get byte arrays and calculate method bytes length
294                 int numbRet = retObj.length;
295                 int retLen = 0;
296                 byte[][] objBytesArr = new byte[numbRet][];
297                 for (int i = 0; i < numbRet; i++) {
298                         // Get byte arrays for the objects
299                         objBytesArr[i] = IoTRMIUtil.getObjectBytes(retObj[i]);
300                         String clsName = retCls[i].getSimpleName();
301                         int retObjLen = rmiUtil.getTypeSize(clsName);
302                         if (retObjLen == -1) {          // indefinite length - store the length first
303                                 retLen = retLen + IoTRMIUtil.RETURN_LEN;
304                         }
305                         retLen = retLen + objBytesArr[i].length;
306                 }
307                 // Construct return in byte array
308                 byte[] retBytes = new byte[retLen];
309                 int pos = 0;
310                 // Iteration for copying bytes
311                 for (int i = 0; i < numbRet; i++) {
312
313                         String clsName = retCls[i].getSimpleName();
314                         int retObjLen = rmiUtil.getTypeSize(clsName);
315                         if (retObjLen == -1) {          // indefinite length
316                                 retObjLen = objBytesArr[i].length;
317                                 byte[] retLenBytes = IoTRMIUtil.intToByteArray(retObjLen);
318                                 System.arraycopy(retLenBytes, 0, retBytes, pos, IoTRMIUtil.RETURN_LEN);
319                                 pos = pos + IoTRMIUtil.RETURN_LEN;
320                         }               
321                         System.arraycopy(objBytesArr[i], 0, retBytes, pos, retObjLen);
322                         pos = pos + retObjLen;
323                 }
324
325                 return retBytes;
326         }
327
328
329         /**
330          * remoteCall() abstract version
331          */
332         public abstract void remoteCall(int objectId, int methodId, Class<?>[] paramCls, Object[] paramObj);
333
334
335         /**
336          * methodToBytes() returns byte representation of a method
337          */
338         public byte[] methodToBytes(int objectId, int methId, Class<?>[] paramCls, Object[] paramObj) {
339
340                 // Initialized to the length of method ID
341                 int methodLen = IoTRMIUtil.OBJECT_ID_LEN;
342                 byte[] objId = IoTRMIUtil.intToByteArray(objectId);
343                 // Get method ID in bytes
344                 byte[] methodId = IoTRMIUtil.intToByteArray(methId);
345                 // Get byte arrays and calculate method bytes length
346                 int numbParam = paramObj.length;
347                 methodLen = methodLen + IoTRMIUtil.METHOD_ID_LEN;
348                 methodLen = methodLen + IoTRMIUtil.PACKET_TYPE_LEN;
349                 byte[][] objBytesArr = new byte[numbParam][];
350                 for (int i = 0; i < numbParam; i++) {
351                         // Get byte arrays for the objects
352                         objBytesArr[i] = IoTRMIUtil.getObjectBytes(paramObj[i]);
353                         String clsName = paramCls[i].getSimpleName();
354                         int paramLen = rmiUtil.getTypeSize(clsName);
355                         if (paramLen == -1) {           // indefinite length - store the length first
356                                 methodLen = methodLen + IoTRMIUtil.PARAM_LEN;
357                         }
358                         methodLen = methodLen + objBytesArr[i].length;
359                 }
360                 // Construct method in byte array
361                 byte[] method = new byte[methodLen];
362                 int pos = 0;
363                 System.arraycopy(objId, 0, method, 0, IoTRMIUtil.METHOD_ID_LEN);
364                 pos = pos + IoTRMIUtil.OBJECT_ID_LEN;
365                 System.arraycopy(methodId, 0, method, pos, IoTRMIUtil.METHOD_ID_LEN);
366                 pos = pos + IoTRMIUtil.METHOD_ID_LEN;
367                 int packetType = IoTRMIUtil.METHOD_TYPE;        // This is a method
368                 byte[] packetTypeBytes = IoTRMIUtil.intToByteArray(packetType);
369                 System.arraycopy(packetTypeBytes, 0, method, pos, IoTRMIUtil.PACKET_TYPE_LEN);
370                 pos = pos + IoTRMIUtil.PACKET_TYPE_LEN;
371                 // Second iteration for copying bytes
372                 for (int i = 0; i < numbParam; i++) {
373
374                         String clsName = paramCls[i].getSimpleName();
375                         int paramLen = rmiUtil.getTypeSize(clsName);
376                         if (paramLen == -1) {           // indefinite length
377                                 paramLen = objBytesArr[i].length;
378                                 byte[] paramLenBytes = IoTRMIUtil.intToByteArray(paramLen);
379                                 System.arraycopy(paramLenBytes, 0, method, pos, IoTRMIUtil.PARAM_LEN);
380                                 pos = pos + IoTRMIUtil.PARAM_LEN;
381                         }               
382                         System.arraycopy(objBytesArr[i], 0, method, pos, paramLen);
383                         pos = pos + paramLen;
384                 }
385
386                 return method;
387         }
388
389
390         /**
391          * getReturnValue() returns return value object
392          */
393         public Object getReturnValue(Class<?> retType, Class<?> retGenTypeVal) {
394
395                 // Receive return value and return it to caller
396                 // Now just strip off the object ID and method ID
397                 int headerLen = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN + IoTRMIUtil.PACKET_TYPE_LEN;
398                 int valByteLen = retValueBytes.length - headerLen;
399                 byte[] retValBytes = new byte[valByteLen];
400                 // Method Id is positioned after object Id in the byte array
401                 Object retObj = null;
402                 if (valByteLen != 0) {
403                         System.arraycopy(retValueBytes, headerLen, retValBytes, 0, valByteLen);
404                         retObj = IoTRMIUtil.getParamObject(retType, retGenTypeVal, retValBytes);
405                 }
406                 // This means the right object and method have gotten the return value, so we set this back to false
407                 return retObj;
408         }
409
410
411         /**
412          * getStructObjects() calls a method remotely by passing in parameters and getting a return Object
413          */
414         public Object[] getStructObjects(Class<?>[] retType, Class<?>[] retGenTypeVal) {
415
416                 // Receive return value and return it to caller
417                 // Now just strip off the object ID and method ID
418                 int headerLen = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN + IoTRMIUtil.PACKET_TYPE_LEN;
419                 int valByteLen = retValueBytes.length - headerLen;
420                 byte[] retValBytes = new byte[valByteLen];
421                 // Method Id is positioned after object Id in the byte array
422                 System.arraycopy(retValueBytes, headerLen, retValBytes, 0, valByteLen);
423                 Object[] retObj = getReturnObjects(retValBytes, retType, retGenTypeVal);
424
425                 return retObj;
426         }
427
428
429         /**
430          * remoteCall() calls a method remotely by passing in parameters and getting a return Object
431          */
432         public Object[] getReturnObjects(byte[] retBytes, Class<?>[] arrCls, Class<?>[] arrGenValCls) {
433
434                 // Byte scanning position
435                 int pos = 0;
436                 Object[] retObj = new Object[arrCls.length];
437                 for (int i=0; i < arrCls.length; i++) {
438
439                         String retType = arrCls[i].getSimpleName();
440                         int retSize = rmiUtil.getTypeSize(retType);
441                         // Get the 32-bit field in the byte array to get the actual
442                         //              length (this is a param with indefinite length)
443                         if (retSize == -1) {
444                                 byte[] bytRetLen = new byte[IoTRMIUtil.RETURN_LEN];
445                                 System.arraycopy(retBytes, pos, bytRetLen, 0, IoTRMIUtil.RETURN_LEN);
446                                 pos = pos + IoTRMIUtil.RETURN_LEN;
447                                 retSize = IoTRMIUtil.byteArrayToInt(bytRetLen);
448                         }
449                         byte[] retObjBytes = new byte[retSize];
450                         System.arraycopy(retBytes, pos, retObjBytes, 0, retSize);
451                         pos = pos + retSize;
452                         retObj[i] = IoTRMIUtil.getParamObject(arrCls[i], arrGenValCls[i], retObjBytes);
453                 }
454
455                 return retObj;
456         }
457
458 }