359b8754c01a6e6ef3ddfb7b3b639b706f2fdd67
[iot2.git] / iotjava / iotruntime / stub / IoTRemoteCall.java
1 package iotruntime.stub;
2
3 // Java libraries
4 import com.sun.net.httpserver.HttpExchange;
5 import com.sun.net.httpserver.HttpHandler;
6 import com.sun.net.httpserver.HttpServer;
7
8 import java.io.BufferedReader;
9 import java.io.InputStreamReader;
10 import java.io.IOException;
11 import java.io.OutputStream;
12
13 import java.net.InetSocketAddress;
14
15 import java.lang.Class;
16 import java.lang.reflect.*;
17
18 // Java JSON - from Maven
19 import org.json.JSONArray;
20 import org.json.JSONException;
21 import org.json.JSONObject;
22
23 import java.util.logging.Level;
24 import java.util.logging.Logger;
25
26 import java.util.Arrays;
27
28
29 /** IoTRemoteCall class that takes JSON packets and instrument
30  *  interfaces used in the code via reflection
31  *
32  * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
33  * @version     1.0                
34  * @since       2016-04-14
35  */
36 public class IoTRemoteCall {
37
38         /**
39          * IoTRemoteCall class properties
40          */
41         final private Class _interface;
42         final private Object _callback;
43         final private int iPort;
44         private static final Logger logger = Logger.getLogger(IoTRemoteCall.class.getName());
45
46         /**
47          * IoTRemoteCall class constants
48          */
49         private final String USER_AGENT = "Mozilla/5.0";
50
51         /**
52          * Constructor
53          */
54         public IoTRemoteCall(Class _interface, Object _callback, int iPort) {
55
56                 this._interface=_interface;
57                 this._callback=_callback;
58                 this.iPort=iPort;
59                 startHttpServer();
60         }
61
62         /**
63          * Get Objects from a HTTP request
64          */
65         private void startHttpServer() {
66                 // Run a separate thread as the HTTP server
67                 IncomingMessageHandler imh=new IncomingMessageHandler(_interface, _callback);
68     
69                 try {
70                         HttpServer server = HttpServer.create(new InetSocketAddress(iPort), 0);
71                         // Context name is according to method name, e.g. getRingStatus
72                         Class<?> inter=_interface;
73                         for (Method m:inter.getDeclaredMethods()) {
74                                 server.createContext("/" + m.getName(), imh);
75                         }
76                         server.setExecutor(null); // creates a default executor
77                         server.start();
78                 } catch (IOException ex) {
79                         ex.printStackTrace();
80                 }
81         }
82
83
84         /**
85          * HTTP server handler
86          */     
87         class IncomingMessageHandler implements HttpHandler {
88                 Class _interface;
89                 Object _callback;
90     
91                 public IncomingMessageHandler(Class _interface, Object _callback) {
92                         this._interface=_interface;
93                         this._callback=_callback;
94                 }
95
96                 @Override
97                 public void handle(HttpExchange t) throws IOException {
98                         BufferedReader brIn = new BufferedReader(new InputStreamReader(t.getRequestBody(), "utf-8"));
99       
100                         String uri = t.getRequestURI().getPath();
101                         String requestMethod = t.getRequestMethod();
102                         StringBuffer sbResponse=null;
103                         if (requestMethod.equalsIgnoreCase("POST")) {
104                                 try {
105                                         String strInputLine;
106                                         sbResponse = new StringBuffer();
107                                         while ((strInputLine = brIn.readLine()) != null) {
108                                                 sbResponse.append(strInputLine);
109                                         }
110                                         brIn.close();
111                                 } catch (IOException e) {
112                                         e.printStackTrace();
113                                 }
114                         }
115                 System.out.println(uri);
116                         try {
117                                 String strJSONString = sbResponse.toString();
118                                 Class[][] cr = new Class[1][];
119                                 Object[] params = decodeJSONArray(strJSONString,cr);
120
121                                 Class<?> c_intrf = _interface;
122                                 Class[] c_params = cr[0];
123       
124                                 Method m = c_intrf.getMethod(uri.substring(1), c_params);
125                                 Object response = m.invoke(_callback, params);
126                                 JSONObject json_r = encodeObject(response);
127       
128                                 // Write a response
129                                 String strResponse = json_r.toString();
130                                 t.sendResponseHeaders(200, strResponse.length());
131                                 OutputStream os = t.getResponseBody();
132                                 os.write(strResponse.getBytes());
133                                 os.close();
134                         } catch (Exception e) {
135                                  e.printStackTrace();
136                                 logger.log(Level.WARNING, "Exception occur", e.getMessage());
137                         }
138                 }
139         }
140
141         /** ==========================
142          * Helper functions
143          *  ==========================/
144
145         /**
146          * Encode JSON String
147          */
148         private static String encodeJSONArray(Object[] array) {
149
150                 try {
151                         // Prepare JSON String as a JSON Object
152                         JSONObject jsonObj = new JSONObject();
153                         JSONArray jsonArr=new JSONArray();
154                         jsonObj.put("params", jsonArr);
155       
156                         // Get the method name and get the array of parameters
157                         for(int i=0;i<array.length;i++) {
158                                 JSONObject obj=encodeObject(array[i]);
159                                 jsonArr.put(i, obj);
160                         }
161                         return jsonObj.toString();
162
163                 } catch (JSONException ex) {
164                         ex.printStackTrace();
165                         //throw new Error("IoTRemoteCall: Exiting");
166                         logger.log(Level.WARNING, "package format error", ex.getMessage());
167                         return null;
168                 }
169         }
170
171         /**
172          * Decode JSON String
173          */
174         private static Object[] decodeJSONArray(String jsonstring, Class[][] carr) {
175                 try {
176                         // Prepare JSON String as a JSON Object
177                         JSONObject jsonObj = new JSONObject(jsonstring);
178                         JSONArray jsonArr = jsonObj.getJSONArray("params");
179                         Object[] retval = new Object[jsonArr.length()];
180                         if (carr != null)
181                                 carr[0] = new Class[retval.length];
182       
183                         // Get the method name and get the array of parameters
184                         for(int i=0;i<jsonArr.length();i++) {
185                                 JSONObject obj = jsonArr.getJSONObject(i);
186                                 Class rc[] = new Class[1];
187                                 retval[i]=decodeObject(obj,rc);
188                                 if (carr!=null)
189                                         carr[0][i]=rc[0];
190                         }
191                         return retval;
192                 } catch (JSONException ex) {
193                         ex.printStackTrace();
194                         //throw new Error("IoTRemoteCall: Exiting");
195                         logger.log(Level.WARNING, "package format error", ex.getMessage());
196                         return null;
197                 }
198         }
199
200         /**
201          * Encode object to JSON
202          */
203         private static JSONObject encodeObject(Object o) {
204
205                 try {
206                         if (o instanceof String ||
207                                 o instanceof Boolean||
208                                 o instanceof Integer||
209                                 o instanceof Long       ||
210                                 o instanceof Double ||
211                                 o instanceof Byte       ||
212                                 o instanceof Float      ||
213                                 o instanceof Short      ||
214                                 o instanceof Character) {
215
216                                 JSONObject jo = new JSONObject();
217                                 Class<?> cl = o.getClass();
218                                 jo.put("type",cl.getName());
219                                 jo.put("value",o);
220                                 return jo;
221                         }
222                         JSONObject jo = new JSONObject();
223                         Class<?> cl = o.getClass();
224                         jo.put("type", cl.getName());
225
226                         JSONArray ja = new JSONArray();
227                         jo.put("fields",ja);
228       
229                         Field[] fields = cl.getFields();
230                         for(int i=0;i<fields.length;i++) {
231                                 Field f = fields[i];
232                                 Object fo = f.get(o);
233
234                                 JSONObject jfo = new JSONObject();
235                                 ja.put(i, jfo);
236                                 jfo.put("name", f.getName());
237                                 jfo.put("value", encodeObject(fo));
238                         }
239                         return jo;
240                 } catch (Exception e) {
241                         e.printStackTrace();
242                         logger.log(Level.WARNING, "package format errors", e.getMessage());
243                         return null;
244                         //throw new Error("IoTRemoteCall: Exiting");
245                 }
246         }
247
248         /**
249          * Decode object from JSON
250          */
251   private static Object decodeObject(JSONObject jsonObj, Class[] tarr) {
252
253                 try {
254                         String type = jsonObj.getString("type");
255                         if (type.equals("java.lang.Integer")    ||
256                                 type.equals("java.lang.Boolean")        ||
257                                 type.equals("java.lang.Long")           ||
258                                 type.equals("java.lang.Character")      ||
259                                 type.equals("java.lang.String")         ||
260                                 type.equals("java.lang.Float")          ||
261                                 type.equals("java.lang.Double")         ||
262                                 type.equals("java.lang.Byte")           ||
263                                 type.equals("java.lang.Short")) {
264
265                                 Class<?> c_type=Class.forName(type);
266                                 if (tarr != null)
267                                         tarr[0] = c_type;
268                                 // TODO: Find a better JSON package later and remove this handler
269                                 // There is a stupid problem with JSON that it strips off the decimal part
270                                 // of the JSON object with the type double when we invoke JSONObject.toString()
271                                 if (type.equals("java.lang.Float") || type.equals("java.lang.Double")) {
272                                         Double temp = Double.parseDouble(jsonObj.get("value").toString());
273                                         return temp;
274                                 } else {
275                                         return jsonObj.get("value");
276                                 }
277                         } else if (type.equals("int")) {
278                                 if (tarr != null)
279                                         tarr[0] = int.class;
280                                 return jsonObj.get("value");
281                         } else if (type.equals("long")) {
282                                 if (tarr != null)
283                                         tarr[0] = long.class;
284                                 return jsonObj.get("value");
285                         } else if (type.equals("short")) {
286                                 if (tarr != null)
287                                         tarr[0] = short.class;
288                                 return jsonObj.get("value");
289                         } else if (type.equals("char")) {
290                                 if (tarr != null)
291                                         tarr[0] = char.class;
292                                 return jsonObj.get("value");
293                         } else if (type.equals("byte")) {
294                                 if (tarr != null)
295                                         tarr[0] = byte.class;
296                                 return jsonObj.get("value");
297                         } else if (type.equals("boolean")) {
298                                 if (tarr != null)
299                                         tarr[0] = boolean.class;
300                                 return jsonObj.get("value");
301                         } else if (type.equals("double")) {
302                                 if (tarr != null)
303                                         tarr[0] = double.class;
304                                 return jsonObj.get("value");
305                         } else if (type.equals("float")) {
306                                 if (tarr != null)
307                                         tarr[0] = float.class;
308                                 return jsonObj.get("value");
309                         }
310       
311                         Class<?> c_type = Class.forName(type);
312                         if (tarr != null)
313                                 tarr[0] = c_type;
314                         Object o = c_type.newInstance();
315                         JSONArray arr = jsonObj.getJSONArray("fields");
316                         for(int i=0;i<arr.length();i++) {
317                                 JSONObject fld = arr.getJSONObject(i);
318                                 String field = fld.getString("name");
319                                 JSONObject obj = fld.getJSONObject("value");
320                                 Object fldo = decodeObject(obj,null);
321                                 Field fobj = c_type.getDeclaredField(field);
322                                 fobj.set(o, fldo);
323                         }
324                         return o;
325                 } catch (Exception e) {
326                         e.printStackTrace();
327                         logger.log(Level.WARNING, "package format error", e.getMessage());
328                         return null;
329                         //throw new Error("IoTRemoteCall: Exiting");
330                 }
331         }
332   
333         interface foo {
334                 int add(int a, int b);
335                 int setRoomID(Integer id);
336                 boolean getRingStatus(Boolean status);
337                 String  getIrrigationInfo(Double inchesPerWeek, Integer weatherZipCode, 
338                         Integer daysToWaterOn, Double inchesPerMinute);
339         }
340
341         static class Fooimpl implements foo {
342
343                 int iRoomID;
344                 boolean bRing;
345                 int A;
346                 int B;
347                 double inchesPerWeek;
348                 int weatherZipCode;
349                 int daysToWaterOn;
350                 double inchesPerMinute;
351
352                 public Fooimpl() {
353                         iRoomID = 0;
354                         bRing = false;
355                         A = 0;
356                         B = 0;
357                 }
358
359                 public int add(int a, int b) {
360                         System.out.println("a="+a+" b="+b);
361                         A = a;
362                         B = b;
363                         System.out.println("A: " + getA());
364                         System.out.println("B: " + getB());
365                         return a+b;
366                 }
367                 public int setRoomID(Integer id) {
368                         System.out.println("Phone in room : " + id);
369                         iRoomID = id;
370                         return id;
371                 }
372                 public boolean getRingStatus(Boolean status) {
373                         System.out.println("Phone rings? " + status);
374                         bRing = status;
375                         return status;
376                 }
377                 public String getIrrigationInfo(Double inchesPerWeek, Integer weatherZipCode,
378                                        Integer daysToWaterOn, Double inchesPerMinute) {
379                         this.inchesPerWeek = inchesPerWeek;
380                         this.weatherZipCode = weatherZipCode;
381                         this.daysToWaterOn = daysToWaterOn;
382                         this.inchesPerMinute = inchesPerMinute;
383                         System.out.println("get Info");
384                         return "info sent";
385                 }       
386                 public void printIDStatus() {
387                         System.out.println("Phone in room : " + iRoomID);
388                         System.out.println("Phone rings? " + bRing);
389                         System.out.println("A: " + A);
390                         System.out.println("B: " + B);
391                 }
392                 public boolean getStatus() {
393                         return bRing;
394                 }
395                 public int getA() {
396                         return A;
397                 }
398                 public int getB() {
399                         return B;
400                 }
401         }
402
403   
404   
405         /* TODO: Uncomment this if we want to do HTTP gateway test
406         public static void main(String[] args) throws Exception {
407
408         Fooimpl fooimp = new Fooimpl();
409                 //IoTRemoteCall iotremcall = new IoTRemoteCall(foo.class, new Fooimpl(), 8000);
410                 new Thread() {
411                         public void run() {
412                                 IoTRemoteCall iotremcall = new IoTRemoteCall(foo.class, fooimp, 8000);  
413                         }
414                 }.start();
415                 System.out.println("server has started!");
416
417                 //while(true) {
418                 //      if (fooimp.getA() > 0) {
419                 //              fooimp.printIDStatus();
420                 //      } else {
421                 //              System.out.println("No change!");
422                 //              Thread.sleep(10000);
423                 //      }
424                 //}
425
426         }*/
427 }
428