Making classes final to make inheritance impossible
[iot2.git] / iotjava / iotruntime / slave / IoTSlave.java
index f1c8deca52030a4b071683bf53e2b5c045610390..7b83d20cc4f6cd1c0996b756422f25ab53202408 100644 (file)
@@ -19,6 +19,7 @@ import java.lang.ClassNotFoundException;
 import java.lang.Class;
 import java.lang.reflect.*;
 import java.lang.ClassLoader;
+import java.net.InetAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
 import java.net.URL;
@@ -30,7 +31,10 @@ import java.rmi.RemoteException;
 import java.rmi.AlreadyBoundException;
 import java.rmi.NotBoundException;
 import java.rmi.server.UnicastRemoteObject;
+import java.util.Arrays;
 import java.util.Properties;
+import java.util.HashMap;
+import java.util.Map;
 
 // Zip/Unzip utility
 import net.lingala.zip4j.exception.ZipException;
@@ -43,13 +47,14 @@ import net.lingala.zip4j.core.ZipFile;
  * @version     1.0
  * @since       2016-06-16
  */
-public class IoTSlave {
+public final class IoTSlave {
 
        /**
         * IoTSlave class properties
         */
        private Message sIoTMasterMsg;
        private String sIoTMasterHostAdd;
+       private String sMainObjectName;
        private int iComPort;
        private int iRMIRegPort;
        private int iRMIStubPort;
@@ -61,6 +66,8 @@ public class IoTSlave {
        private Socket socket;
        private ObjectOutputStream outStream;
        private ObjectInputStream inStream;
+       private Map<String,Object> mapObjNameStub;
+
        /**
         * IoTSet object, e.g. IoTSet<ProximitySensor> proximity_sensors;
         * IoTRelation object, e.g. IoTRelation<ProximitySensor, LightBulb> ps_lb_relation;
@@ -74,7 +81,10 @@ public class IoTSlave {
        private static String STR_JAR_FILE_PATH;
        private static String STR_OBJ_CLS_PFX;
        private static String STR_INTERFACE_PFX;
+       private static String SKEL_CLASS_SUFFIX;
+       private static String STUB_CLASS_SUFFIX;
        private static boolean BOOL_VERBOSE;
+       private static boolean CAPAB_BASED_RMI;
 
        /**
         * IoTSlave class constants - not to be changed by users
@@ -100,6 +110,7 @@ public class IoTSlave {
                iComPort = Integer.parseInt(argInp[1]);
                iRMIRegPort = Integer.parseInt(argInp[2]);
                iRMIStubPort = Integer.parseInt(argInp[3]);
+               sMainObjectName = null;
                strFieldName = null;
                clsMain = null;
                objMainCls = null;
@@ -112,11 +123,15 @@ public class IoTSlave {
                socket = null;
                outStream = null;
                inStream = null;
+               mapObjNameStub = new HashMap<String,Object>();
 
                STR_JAR_FILE_PATH = null;
                STR_OBJ_CLS_PFX = null;
                STR_INTERFACE_PFX = null;
+               SKEL_CLASS_SUFFIX = null;
+               STUB_CLASS_SUFFIX = null;
                BOOL_VERBOSE = false;
+               CAPAB_BASED_RMI = false;
        }
 
        /**
@@ -141,14 +156,21 @@ public class IoTSlave {
                STR_JAR_FILE_PATH = prop.getProperty("JAR_FILE_PATH");
                STR_OBJ_CLS_PFX = prop.getProperty("OBJECT_CLASS_PREFIX");
                STR_INTERFACE_PFX = prop.getProperty("INTERFACE_PREFIX");
-               STR_INTERFACE_PFX = prop.getProperty("INTERFACE_PREFIX");
+               SKEL_CLASS_SUFFIX = prop.getProperty("SKEL_CLASS_SUFFIX");
+               STUB_CLASS_SUFFIX = prop.getProperty("STUB_CLASS_SUFFIX");
                if (prop.getProperty("VERBOSE").equals(STR_YES)) {
                        BOOL_VERBOSE = true;
                }
+               if (prop.getProperty("CAPAB_BASED_RMI").equals(STR_YES)) {
+                       CAPAB_BASED_RMI = true;
+               }
 
                System.out.println("JAR_FILE_PATH=" + STR_JAR_FILE_PATH);
                System.out.println("OBJECT_CLASS_PREFIX=" + STR_OBJ_CLS_PFX);
                System.out.println("INTERFACE_PREFIX=" + STR_INTERFACE_PFX);
+               System.out.println("SKEL_CLASS_SUFFIX=" + SKEL_CLASS_SUFFIX);
+               System.out.println("STUB_CLASS_SUFFIX=" + STUB_CLASS_SUFFIX);
+               System.out.println("CAPAB_BASED_RMI=" + CAPAB_BASED_RMI);
                System.out.println("IoTMaster: Information extracted successfully!");
        }
 
@@ -177,6 +199,40 @@ public class IoTSlave {
                }
        }
 
+       /**
+        * A private method to create object
+        *
+        * @return  void
+        */
+       private void createCapabBasedRMIJava(MessageCreateObject sMessage) throws 
+               ClassNotFoundException, NoSuchMethodException, UnknownHostException {
+
+               // Instantiate the skeleton and put in the object
+               String strObjSkelName = STR_OBJ_CLS_PFX + "." + sMessage.getObjectClass() +
+                                                                       "." + sMessage.getObjectInterfaceName() + SKEL_CLASS_SUFFIX;
+               RuntimeOutput.print("IoTSlave: Skeleton object: " + strObjSkelName, BOOL_VERBOSE);
+               Class<?> clsSkel = Class.forName(strObjSkelName);
+               Class<?> clsInt = Class.forName(STR_OBJ_CLS_PFX + "." + STR_INTERFACE_PFX + 
+                       "." + sMessage.getObjectInterfaceName());
+               Class[] clsSkelParams = { clsInt, int.class, int.class };       // Port number is integer
+               Constructor<?> objSkelCons = clsSkel.getDeclaredConstructor(clsSkelParams);
+               Object objSkelParams[] = { objMainCls, iRMIStubPort, iRMIRegPort };
+               // Create a new thread for each skeleton
+               Thread objectThread = new Thread(new Runnable() {
+                       public void run() {
+                               try {
+                                       Object objSkel = objSkelCons.newInstance(objSkelParams);
+                               } catch (InstantiationException |
+                                                IllegalAccessException |
+                                                InvocationTargetException ex) {
+                                       ex.printStackTrace();
+                               }
+                       }
+               });
+               objectThread.start();
+               RuntimeOutput.print("IoTSlave: Done generating object!", BOOL_VERBOSE);
+       }
+
        /**
         * A private method to create object
         *
@@ -189,7 +245,6 @@ public class IoTSlave {
 
                // Translating into the actual Message class
                MessageCreateObject sMessage = (MessageCreateObject) sIoTMasterMsg;
-
                // Instantiate object using reflection
                String strObjClassName = STR_OBJ_CLS_PFX + "." + sMessage.getObjectClass() +
                                                                                                                 "." + sMessage.getObjectClass();
@@ -198,18 +253,23 @@ public class IoTSlave {
                                                                                         sMessage.getObjectClass() + STR_JAR_FILE_EXT, BOOL_VERBOSE);
                addURL(file.toURI().toURL());
                clsMain = Class.forName(strObjClassName);
-
                Class[] clsParams = sMessage.getObjectFldCls();
                Constructor<?> ct = clsMain.getDeclaredConstructor(clsParams);
                Object objParams[] = sMessage.getObjectFields();
                objMainCls = ct.newInstance(objParams);
-               RuntimeOutput.print("IoTSlave: Create object!", BOOL_VERBOSE);
-
-               // Register object to RMI - there are 2 ports: RMI registry port and RMI stub port
-               Object objStub = (Object)
-                       UnicastRemoteObject.exportObject((Remote) objMainCls, iRMIStubPort);
-               Registry registry = LocateRegistry.createRegistry(iRMIRegPort);
-               registry.bind(sMessage.getObjectName(), (Remote) objStub);
+               RuntimeOutput.print("IoTSlave: Creating RMI skeleton: " +
+                       sMessage.getHostAddress() + ":" + sMessage.getRMIRegPort() +
+                       " with RMI stub port: " + iRMIStubPort, BOOL_VERBOSE);
+               if (CAPAB_BASED_RMI) {
+               // Use the new capability-based RMI in Java
+                       createCapabBasedRMIJava(sMessage);
+               } else {
+                       // Register object to RMI - there are 2 ports: RMI registry port and RMI stub port
+                       Object objStub = (Object)
+                               UnicastRemoteObject.exportObject((Remote) objMainCls, iRMIStubPort);
+                       Registry registry = LocateRegistry.createRegistry(iRMIRegPort);
+                       registry.bind(sMessage.getObjectName(), (Remote) objStub);
+               }
                outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
                RuntimeOutput.print("IoTSlave: Registering object via RMI!", BOOL_VERBOSE);
 
@@ -274,7 +334,8 @@ public class IoTSlave {
                addURL(file.toURI().toURL());
                // We will always have a package name <object name>.<object name>
                // e.g. SmartLightsController.SmartLightsController
-               clsMain = Class.forName(sMessage.getObjectName() + "." + sMessage.getObjectName());
+               sMainObjectName = sMessage.getObjectName();
+               clsMain = Class.forName(sMainObjectName + "." + sMainObjectName);
                objMainCls = clsMain.newInstance();
 
                // Send back the received message as acknowledgement
@@ -360,22 +421,69 @@ public class IoTSlave {
                return stubObjConv;
        }
 
+       /**
+        * A private method to get an object and create a stub
+        * <p>
+        * This is using the capability-based RMI skeleton and stub scheme
+        *
+        * @return  Object
+        */
+       private Object getObjectFromStub() throws RemoteException,
+                       ClassNotFoundException, NoSuchMethodException, InstantiationException, 
+                       IllegalAccessException, NotBoundException, InvocationTargetException, UnknownHostException {
+
+               // Translating into the actual Message class
+               MessageGetObject sMessage = (MessageGetObject) sIoTMasterMsg;
+               Object stubObjConv = null;
+               String strObjectName = sMessage.getObjectName();
+               String strObjClassInterfaceName = STR_OBJ_CLS_PFX + "." + STR_INTERFACE_PFX + "." +
+                       sMessage.getObjectStubInterfaceName();
+               Class<?> clsInf = Class.forName(strObjClassInterfaceName);
+               if (mapObjNameStub.containsKey(strObjectName)) {
+                       RuntimeOutput.print("IoTSlave: Getting back object on slave: " + strObjectName, BOOL_VERBOSE);
+                       stubObjConv = clsInf.cast(mapObjNameStub.get(strObjectName));
+               } else {
+                       // Instantiate the stub and put in the object
+                       String strObjStubName = sMainObjectName + "." + sMessage.getObjectStubInterfaceName() + STUB_CLASS_SUFFIX;
+                       Class<?> clsStub = Class.forName(strObjStubName);       // Port number is integer
+                       Class[] clsStubParams = { int.class, int.class, int.class, int.class, String.class, int.class };
+                       Constructor<?> objStubCons = clsStub.getDeclaredConstructor(clsStubParams);
+
+                       int rev = 0;
+                       Object objStubParams[] = { 0, 0, sMessage.getRMIStubPort(), sMessage.getRMIRegPort(), sMessage.getHostAddress(), rev };
+                       RuntimeOutput.print("IoTSlave: Creating RMI stub: " +
+                               sMessage.getHostAddress() + ":" + sMessage.getRMIRegPort() + 
+                               " and RMI stub port: " + sMessage.getRMIStubPort(), BOOL_VERBOSE);
+                       Object stubObj = objStubCons.newInstance(objStubParams);
+                       // Class conversion to interface class of this class,
+                       // e.g. ProximitySensorImpl has ProximitySensor interface
+                       RuntimeOutput.print("IoTSlave: Registering new stub object: " + strObjectName, BOOL_VERBOSE);
+                       mapObjNameStub.put(strObjectName, stubObj);
+                       stubObjConv = clsInf.cast(stubObj);
+               }
+
+               return stubObjConv;
+       }
+
        /**
         * A private method to get an IoTSet object
         *
         * @return  void
         */
        private void getIoTSetObject() throws IOException,
-               ClassNotFoundException, RemoteException, NotBoundException {
-
-               Object objRegistry = getObjectFromRegistry();
+               ClassNotFoundException, RemoteException, NotBoundException, NoSuchMethodException,
+               InstantiationException, IllegalAccessException, InvocationTargetException {
+               Object objRegistry = null;
+               if (CAPAB_BASED_RMI)
+                       objRegistry = getObjectFromStub();
+               else
+                       objRegistry = getObjectFromRegistry();
                isetObject.add(objRegistry);
                RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
 
                // Send back the received message as acknowledgement
                outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
                RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
-
        }
 
        /**
@@ -384,9 +492,13 @@ public class IoTSlave {
         * @return  void
         */
        private void getIoTRelationFirstObject() throws IOException,
-               ClassNotFoundException, RemoteException, NotBoundException {
-
-               Object objRegistry = getObjectFromRegistry();
+               ClassNotFoundException, RemoteException, NotBoundException, NoSuchMethodException,
+               InstantiationException, IllegalAccessException, InvocationTargetException {
+               Object objRegistry = null;
+               if (CAPAB_BASED_RMI)
+                       objRegistry = getObjectFromStub();
+               else
+                       objRegistry = getObjectFromRegistry();
                iRelFirstObject = objRegistry;
 
                // Send back the received message as acknowledgement
@@ -401,9 +513,13 @@ public class IoTSlave {
         * @return  void
         */
        private void getIoTRelationSecondObject() throws IOException,
-               ClassNotFoundException, RemoteException, NotBoundException {
-
-               Object objRegistry = getObjectFromRegistry();
+               ClassNotFoundException, RemoteException, NotBoundException, NoSuchMethodException,
+               InstantiationException, IllegalAccessException, InvocationTargetException {
+               Object objRegistry = null;
+               if (CAPAB_BASED_RMI)
+                       objRegistry = getObjectFromStub();
+               else
+                       objRegistry = getObjectFromRegistry();
                iRelSecondObject = objRegistry;
 
                // Now add the first and the second object into IoTRelation
@@ -426,7 +542,6 @@ public class IoTSlave {
 
                // Reinitialize IoTSet field after getting all the objects
                iotsetObject = new IoTSet<Object>(isetObject.values());
-
                // Private fields need getDeclaredField(), while public fields use getField()
                Field fld = clsMain.getDeclaredField(strFieldName);
                boolean bAccess = fld.isAccessible();
@@ -451,7 +566,6 @@ public class IoTSlave {
 
                // Reinitialize IoTSet field after getting all the objects
                iotrelObject = new IoTRelation<Object,Object>(irelObject.relationMap(), irelObject.size());
-
                // Private fields need getDeclaredField(), while public fields use getField()
                Field fld = clsMain.getDeclaredField(strFieldName);
                boolean bAccess = fld.isAccessible();
@@ -477,7 +591,6 @@ public class IoTSlave {
 
                // Translating into the actual Message class
                MessageGetDeviceObject sMessage = (MessageGetDeviceObject) sIoTMasterMsg;
-
                // Get IoTSet objects for IP address set on device driver/controller
                IoTDeviceAddress objDeviceAddress = new IoTDeviceAddress(sMessage.getHostAddress(),
                        sMessage.getSourceDeviceDriverPort(),
@@ -505,7 +618,6 @@ public class IoTSlave {
 
                // Translating into the actual Message class
                MessageGetSimpleDeviceObject sMessage = (MessageGetSimpleDeviceObject) sIoTMasterMsg;
-
                // Get IoTSet objects for IP address set on device driver/controller
                IoTZigbeeAddress objZBDevAddress = new IoTZigbeeAddress(sMessage.getHostAddress());
                RuntimeOutput.print("IoTSlave: Device address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
@@ -528,7 +640,6 @@ public class IoTSlave {
 
                // Translating into the actual Message class
                MessageGetSimpleDeviceObject sMessage = (MessageGetSimpleDeviceObject) sIoTMasterMsg;
-
                // Get IoTSet objects for IP address set on device driver/controller
                IoTAddress objAddress = new IoTAddress(sMessage.getHostAddress());
                RuntimeOutput.print("IoTSlave: Address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
@@ -666,6 +777,8 @@ public class IoTSlave {
                        inStream.close();
                        socket.close();
                        RuntimeOutput.print("IoTSlave: Closing!", BOOL_VERBOSE);
+                       // We have to continuously loop because we are preserving our stubs and skeletons
+                       //while(true) { }
 
                } catch (IOException               |
                                 ClassNotFoundException    |