Adding premature C++ side; supporting only primitive types (including string) for now
[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
14 /** Class IoTRMIObject is a class that stores info of an object.
15  *  <p>
16  *  It stores object ID, methods, method ID, method's signature 
17  *  and parameters.
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 
21  *
22  * @author      Rahmadi Trimananda <rtrimana @ uci.edu>
23  * @version     1.0
24  * @since       2016-10-03
25  */
26 public class IoTRMIObject {
27
28         /**
29          * Class Properties
30          */
31         private Map<Integer,String> mapHash2Sign;       // Map from hashcode(method ID) to signature
32         private IoTRMIUtil rmiUtil;
33         private IoTSocketServer rmiServer;
34         private byte[] methodBytes;
35
36
37         /**
38          * Constructors
39          */
40         public IoTRMIObject(int _port, String[] _methodSign) throws  
41                 ClassNotFoundException, InstantiationException, 
42                         IllegalAccessException, IOException {
43
44                 rmiUtil = new IoTRMIUtil();
45                 mapHash2Sign = new HashMap<Integer,String>();
46                 methodBytes = null;
47                 getMethodIds(_methodSign);      // Initialize the method ID map
48                 rmiServer = new IoTSocketServer(_port);
49                 rmiServer.connect();
50         }
51
52
53         /**
54          * sendReturnObj() sends back return Object to client
55          */
56         public void sendReturnObj(Object retObj) throws IOException {
57
58                 // Send back return value
59                 byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
60                 rmiServer.sendBytes(retObjBytes);
61         }
62
63
64         /**
65          * getMethodBytes() waits for method transmission in bytes
66          */
67         public void getMethodBytes() throws IOException {
68
69                 // Receive method info
70                 methodBytes = rmiServer.receiveBytes(methodBytes);
71         }
72
73
74         /**
75          * getSignature() gets method signature from bytes
76          */
77         public String getSignature() {
78
79                 // Get method Id bytes
80                 byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
81                 System.arraycopy(methodBytes, 0, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
82                 // Get method Id
83                 int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
84                 // Get method signature from the Map
85                 return mapHash2Sign.get(methodId);
86         }
87
88
89         /**
90          * getMethodParams() gets method params based on byte array received
91          * <p>
92          * Basically this is the format of a method in bytes:
93          * 1) 32-bit value of method ID (hash code)
94          * 2) m parameters with n-bit value each (m x n-bit)
95          * For the parameters that don't have definite length,
96          * we need to extract the length from a preceding 32-bit
97          * field in front of it.
98          *
99          * For primitive objects:
100          * | 32-bit method ID | m-bit actual data (fixed length)  |
101          * 
102          * For string, arrays, and non-primitive objects:
103          * | 32-bit method ID | 32-bit length | n-bit actual data | ...
104          * 
105          */
106         public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenKeyCls, Class<?>[] arrGenValCls) {
107
108                 // Byte scanning position
109                 int pos = IoTRMIUtil.METHOD_ID_LEN;
110                 Object[] paramObj = new Object[arrCls.length];
111                 for (int i=0; i < arrCls.length; i++) {
112
113                         String paramType = arrCls[i].getSimpleName();
114                         int paramSize = rmiUtil.getTypeSize(paramType);
115                         // Get the 32-bit field in the byte array to get the actual
116                         //              length (this is a param with indefinite length)
117                         if (paramSize == -1) {
118                                 byte[] bytPrmLen = new byte[IoTRMIUtil.PARAM_LEN];
119                                 System.arraycopy(methodBytes, pos, bytPrmLen, 0, IoTRMIUtil.PARAM_LEN);
120                                 pos = pos + IoTRMIUtil.PARAM_LEN;
121                                 paramSize = IoTRMIUtil.byteArrayToInt(bytPrmLen);
122                         }
123                         byte[] paramBytes = new byte[paramSize];
124                         System.arraycopy(methodBytes, pos, paramBytes, 0, paramSize);
125                         pos = pos + paramSize;
126                         paramObj[i] = IoTRMIUtil.getParamObject(arrCls[i], arrGenKeyCls[i], 
127                                 arrGenValCls[i], paramBytes);
128                 }
129
130                 return paramObj;
131         }
132         
133
134         /**
135          * getMethodIds() gets methods identifiers (hash code) and store them in the Map
136          */
137         private void getMethodIds(String[] methodSign) {
138
139                 for (String sign : methodSign) {
140                         byte[] hashCode = IoTRMIUtil.getHashCodeBytes(sign);
141                         int methodId = IoTRMIUtil.byteArrayToInt(hashCode);
142                         mapHash2Sign.put(methodId, sign);
143                 }
144         }
145 }