Adding last version of iotruntime and iotinstaller; preparing to extend IoTMaster...
[iot2.git] / iotjava / iotruntime / stub / IoTRemoteCall.java
diff --git a/iotjava/iotruntime/stub/IoTRemoteCall.java b/iotjava/iotruntime/stub/IoTRemoteCall.java
new file mode 100644 (file)
index 0000000..0b261ff
--- /dev/null
@@ -0,0 +1,481 @@
+package iotruntime.stub;
+
+// Java libraries
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsParameters;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.TrustManagerFactory;
+
+import java.lang.Class;
+import java.lang.reflect.*;
+
+// Java JSON - from Maven
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import java.util.Arrays;
+
+
+/** IoTRemoteCall class that takes JSON packets and instrument
+ *  interfaces used in the code via reflection
+ *
+ * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
+ * @version     1.0                
+ * @since       2016-04-14
+ */
+public class IoTRemoteCall {
+
+       /**
+        * IoTRemoteCall class properties
+        */
+       final private Class _interface;
+       final private Object _callback;
+       final private int iPort;
+       final private String strAddress;
+       private static final Logger logger = Logger.getLogger(IoTRemoteCall.class.getName());
+
+       /**
+        * IoTRemoteCall class constants
+        */
+       private final String USER_AGENT = "Mozilla/5.0";
+       private final String PASSWORD = "password";
+       private final String KEYEXT = ".jks";
+       private final String KEYTYPE = "SunX509";
+       private final String KEYINSTANCE = "JKS";
+
+       /**
+        * Constructor
+        */
+       public IoTRemoteCall(Class _interface, Object _callback, int _iPort, String _strAddress) {
+
+               this._interface = _interface;
+               this._callback = _callback;
+               this.iPort = _iPort;
+               this.strAddress = _strAddress;
+               startHttpsServer();
+       }
+
+       /**
+        * Get Objects from a HTTP request
+        */
+       private void startHttpsServer() {
+               // Run a separate thread as the HTTP server
+               IncomingMessageHandler imh=new IncomingMessageHandler(_interface, _callback);
+    
+               try {
+                       HttpsServer server = HttpsServer.create(new InetSocketAddress(iPort), 0);
+                       SSLContext sslContext = SSLContext.getInstance("TLS");
+
+            // initialise the keystore
+            char[] password = PASSWORD.toCharArray();
+            KeyStore ks = KeyStore.getInstance(KEYINSTANCE);
+            FileInputStream fis = new FileInputStream(strAddress + KEYEXT);
+            ks.load(fis, password);
+
+            // setup the key manager factory
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEYTYPE);
+            kmf.init(ks, password);
+
+            // setup the trust manager factory
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(KEYTYPE);
+            tmf.init(ks);
+
+            // setup the HTTPS context and parameters
+            sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+            server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
+                public void configure(HttpsParameters params) {
+                    try {
+                        // initialise the SSL context
+                        SSLContext c = SSLContext.getDefault();
+                        SSLEngine engine = c.createSSLEngine();
+                        params.setNeedClientAuth(false);
+                        params.setCipherSuites(engine.getEnabledCipherSuites());
+                        params.setProtocols(engine.getEnabledProtocols());
+
+                        // get the default parameters
+                        SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
+                        params.setSSLParameters(defaultSSLParameters);
+
+                    } catch (Exception ex) {
+                        ex.printStackTrace();
+                    }
+                }
+            });
+
+                       // Context name is according to method name, e.g. getRingStatus
+                       Class<?> inter=_interface;
+                       for (Method m:inter.getDeclaredMethods()) {
+                               server.createContext("/" + m.getName(), imh);
+                       }
+                       server.setExecutor(null); // creates a default executor
+                       server.start();
+               } catch (Exception ex) {
+                       ex.printStackTrace();
+               }
+       }
+
+
+       /**
+        * HTTP server handler
+        */     
+       class IncomingMessageHandler implements HttpHandler {
+               Class _interface;
+               Object _callback;
+    
+               public IncomingMessageHandler(Class _interface, Object _callback) {
+                       this._interface=_interface;
+                       this._callback=_callback;
+               }
+
+               @Override
+               public void handle(HttpExchange t) throws IOException {
+                       BufferedReader brIn = new BufferedReader(new InputStreamReader(t.getRequestBody(), "utf-8"));
+      
+                       String uri = t.getRequestURI().getPath();
+                       String requestMethod = t.getRequestMethod();
+                       StringBuffer sbResponse=null;
+                       if (requestMethod.equalsIgnoreCase("POST")) {
+                               try {
+                                       String strInputLine;
+                                       sbResponse = new StringBuffer();
+                                       while ((strInputLine = brIn.readLine()) != null) {
+                                               sbResponse.append(strInputLine);
+                                       }
+                                       brIn.close();
+                               } catch (IOException e) {
+                                       e.printStackTrace();
+                               }
+                       }
+               System.out.println(uri);
+                       try {
+                               String strJSONString = sbResponse.toString();
+                               System.out.println(strJSONString);
+                               Class[][] cr = new Class[1][];
+                               Object[] params = decodeJSONArray(strJSONString,cr);
+
+                               Class<?> c_intrf = _interface;
+                               Class[] c_params = cr[0];
+      
+                               Method m = c_intrf.getMethod(uri.substring(1), c_params);
+                               Object response = m.invoke(_callback, params);
+                               JSONObject json_r = encodeObject(response);
+      
+                               // Write a response
+                               String strResponse = json_r.toString();
+                               t.sendResponseHeaders(200, strResponse.length());
+                               OutputStream os = t.getResponseBody();
+                               os.write(strResponse.getBytes());
+                               os.close();
+                       } catch (Exception e) {
+                                e.printStackTrace();
+                               logger.log(Level.WARNING, "Exception occur", e.getMessage());
+                       }
+               }
+       }
+
+       /** ==========================
+        * Helper functions
+        *  ==========================/
+
+       /**
+        * Encode JSON String
+        */
+       private static String encodeJSONArray(Object[] array) {
+
+               try {
+                       // Prepare JSON String as a JSON Object
+                       JSONObject jsonObj = new JSONObject();
+                       JSONArray jsonArr=new JSONArray();
+                       jsonObj.put("params", jsonArr);
+      
+                       // Get the method name and get the array of parameters
+                       for(int i=0;i<array.length;i++) {
+                               JSONObject obj=encodeObject(array[i]);
+                               jsonArr.put(i, obj);
+                       }
+                       return jsonObj.toString();
+
+               } catch (JSONException ex) {
+                       ex.printStackTrace();
+                       //throw new Error("IoTRemoteCall: Exiting");
+                       logger.log(Level.WARNING, "package format error", ex.getMessage());
+                       return null;
+               }
+       }
+
+       /**
+        * Decode JSON String
+        */
+       private static Object[] decodeJSONArray(String jsonstring, Class[][] carr) {
+               try {
+                       // Prepare JSON String as a JSON Object
+                       JSONObject jsonObj = new JSONObject(jsonstring);
+                       JSONArray jsonArr = jsonObj.getJSONArray("params");
+                       Object[] retval = new Object[jsonArr.length()];
+                       if (carr != null)
+                               carr[0] = new Class[retval.length];
+      
+                       // Get the method name and get the array of parameters
+                       for(int i=0;i<jsonArr.length();i++) {
+                               JSONObject obj = jsonArr.getJSONObject(i);
+                               Class rc[] = new Class[1];
+                               retval[i]=decodeObject(obj,rc);
+                               if (carr!=null)
+                                       carr[0][i]=rc[0];
+                       }
+                       return retval;
+               } catch (JSONException ex) {
+                       ex.printStackTrace();
+                       //throw new Error("IoTRemoteCall: Exiting");
+                       logger.log(Level.WARNING, "package format error", ex.getMessage());
+                       return null;
+               }
+       }
+
+       /**
+        * Encode object to JSON
+        */
+       private static JSONObject encodeObject(Object o) {
+
+               try {
+                       if (o instanceof String ||
+                               o instanceof Boolean||
+                               o instanceof Integer||
+                               o instanceof Long       ||
+                               o instanceof Double ||
+                               o instanceof Byte       ||
+                               o instanceof Float      ||
+                               o instanceof Short      ||
+                               o instanceof Character) {
+
+                               JSONObject jo = new JSONObject();
+                               Class<?> cl = o.getClass();
+                               jo.put("type",cl.getName());
+                               jo.put("value",o);
+                               return jo;
+                       }
+                       JSONObject jo = new JSONObject();
+                       Class<?> cl = o.getClass();
+                       jo.put("type", cl.getName());
+
+                       JSONArray ja = new JSONArray();
+                       jo.put("fields",ja);
+      
+                       Field[] fields = cl.getFields();
+                       for(int i=0;i<fields.length;i++) {
+                               Field f = fields[i];
+                               Object fo = f.get(o);
+
+                               JSONObject jfo = new JSONObject();
+                               ja.put(i, jfo);
+                               jfo.put("name", f.getName());
+                               jfo.put("value", encodeObject(fo));
+                       }
+                       return jo;
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       logger.log(Level.WARNING, "package format errors", e.getMessage());
+                       return null;
+                       //throw new Error("IoTRemoteCall: Exiting");
+               }
+       }
+
+       /**
+        * Decode object from JSON
+        */
+  private static Object decodeObject(JSONObject jsonObj, Class[] tarr) {
+
+               try {
+                       String type = jsonObj.getString("type");
+                       if (type.equals("java.lang.Integer")    ||
+                               type.equals("java.lang.Boolean")        ||
+                               type.equals("java.lang.Long")           ||
+                               type.equals("java.lang.Character")      ||
+                               type.equals("java.lang.String")         ||
+                               type.equals("java.lang.Float")          ||
+                               type.equals("java.lang.Double")         ||
+                               type.equals("java.lang.Byte")           ||
+                               type.equals("java.lang.Short")) {
+
+                               Class<?> c_type=Class.forName(type);
+                               if (tarr != null)
+                                       tarr[0] = c_type;
+                               // TODO: Find a better JSON package later and remove this handler
+                               // There is a stupid problem with JSON that it strips off the decimal part
+                               // of the JSON object with the type double when we invoke JSONObject.toString()
+                               if (type.equals("java.lang.Float") || type.equals("java.lang.Double")) {
+                                       Double temp = Double.parseDouble(jsonObj.get("value").toString());
+                                       return temp;
+                               } else {
+                                       return jsonObj.get("value");
+                               }
+                       } else if (type.equals("int")) {
+                               if (tarr != null)
+                                       tarr[0] = int.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("long")) {
+                               if (tarr != null)
+                                       tarr[0] = long.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("short")) {
+                               if (tarr != null)
+                                       tarr[0] = short.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("char")) {
+                               if (tarr != null)
+                                       tarr[0] = char.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("byte")) {
+                               if (tarr != null)
+                                       tarr[0] = byte.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("boolean")) {
+                               if (tarr != null)
+                                       tarr[0] = boolean.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("double")) {
+                               if (tarr != null)
+                                       tarr[0] = double.class;
+                               return jsonObj.get("value");
+                       } else if (type.equals("float")) {
+                               if (tarr != null)
+                                       tarr[0] = float.class;
+                               return jsonObj.get("value");
+                       }
+      
+                       Class<?> c_type = Class.forName(type);
+                       if (tarr != null)
+                               tarr[0] = c_type;
+                       Object o = c_type.newInstance();
+                       JSONArray arr = jsonObj.getJSONArray("fields");
+                       for(int i=0;i<arr.length();i++) {
+                               JSONObject fld = arr.getJSONObject(i);
+                               String field = fld.getString("name");
+                               JSONObject obj = fld.getJSONObject("value");
+                               Object fldo = decodeObject(obj,null);
+                               Field fobj = c_type.getDeclaredField(field);
+                               fobj.set(o, fldo);
+                       }
+                       return o;
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       logger.log(Level.WARNING, "package format error", e.getMessage());
+                       return null;
+                       //throw new Error("IoTRemoteCall: Exiting");
+               }
+       }
+  
+       interface foo {
+               int add(int a, int b);
+               int setRoomID(Integer id);
+               boolean getRingStatus(Boolean status);
+               String  getIrrigationInfo(Double inchesPerWeek, Integer weatherZipCode, 
+                       Integer daysToWaterOn, Double inchesPerMinute);
+       }
+
+       static class Fooimpl implements foo {
+
+               int iRoomID;
+               boolean bRing;
+               int A;
+               int B;
+               double inchesPerWeek;
+               int weatherZipCode;
+               int daysToWaterOn;
+               double inchesPerMinute;
+
+               public Fooimpl() {
+                       iRoomID = 0;
+                       bRing = false;
+                       A = 0;
+                       B = 0;
+               }
+
+               public int add(int a, int b) {
+                       System.out.println("a="+a+" b="+b);
+                       A = a;
+                       B = b;
+                       System.out.println("A: " + getA());
+                       System.out.println("B: " + getB());
+                       return a+b;
+               }
+               public int setRoomID(Integer id) {
+                       System.out.println("Phone in room : " + id);
+                       iRoomID = id;
+                       return id;
+               }
+               public boolean getRingStatus(Boolean status) {
+                       System.out.println("Phone rings? " + status);
+                       bRing = status;
+                       return status;
+               }
+               public String getIrrigationInfo(Double inchesPerWeek, Integer weatherZipCode,
+                                      Integer daysToWaterOn, Double inchesPerMinute) {
+                       this.inchesPerWeek = inchesPerWeek;
+                       this.weatherZipCode = weatherZipCode;
+                       this.daysToWaterOn = daysToWaterOn;
+                       this.inchesPerMinute = inchesPerMinute;
+                       System.out.println("get Info");
+                       return "info sent";
+               }       
+               public void printIDStatus() {
+                       System.out.println("Phone in room : " + iRoomID);
+                       System.out.println("Phone rings? " + bRing);
+                       System.out.println("A: " + A);
+                       System.out.println("B: " + B);
+               }
+               public boolean getStatus() {
+                       return bRing;
+               }
+               public int getA() {
+                       return A;
+               }
+               public int getB() {
+                       return B;
+               }
+       }
+
+  
+  
+       public static void main(String[] args) throws Exception {
+
+       Fooimpl fooimp = new Fooimpl();
+               //IoTRemoteCall iotremcall = new IoTRemoteCall(foo.class, new Fooimpl(), 8000);
+               new Thread() {
+                       public void run() {
+                               IoTRemoteCall iotremcall = new IoTRemoteCall(foo.class, fooimp, 8000, "192.168.2.244"); 
+                       }
+               }.start();
+               System.out.println("server has started!");
+
+               //while(true) {
+               //      if (fooimp.getA() > 0) {
+               //              fooimp.printIDStatus();
+               //      } else {
+               //              System.out.println("No change!");
+               //              Thread.sleep(10000);
+               //      }
+               //}
+
+       }
+}