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