First benchmark working with capability-based RMI; adjustments in callbacks, etc.
[iot2.git] / iotjava / iotruntime / slave / IoTSlave.java
1 package iotruntime.slave;
2
3 import iotruntime.*;
4 import iotruntime.zigbee.*;
5 import iotruntime.messages.*;
6 import iotruntime.master.RuntimeOutput;
7
8 // Java packages
9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.FileOutputStream;
12 import java.io.ObjectInputStream;
13 import java.io.ObjectOutputStream;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.io.IOException;
17 import java.io.FileNotFoundException;
18 import java.lang.ClassNotFoundException;
19 import java.lang.Class;
20 import java.lang.reflect.*;
21 import java.lang.ClassLoader;
22 import java.net.InetAddress;
23 import java.net.Socket;
24 import java.net.UnknownHostException;
25 import java.net.URL;
26 import java.net.URLClassLoader;
27 import java.rmi.registry.LocateRegistry;
28 import java.rmi.registry.Registry;
29 import java.rmi.Remote;
30 import java.rmi.RemoteException;
31 import java.rmi.AlreadyBoundException;
32 import java.rmi.NotBoundException;
33 import java.rmi.server.UnicastRemoteObject;
34 import java.util.Arrays;
35 import java.util.Properties;
36 import java.util.HashMap;
37 import java.util.Map;
38
39 // Zip/Unzip utility
40 import net.lingala.zip4j.exception.ZipException;
41 import net.lingala.zip4j.core.ZipFile;
42
43 /** Class IoTSlave is run by IoTMaster on a different JVM's.
44  *  It needs to respond to IoTMaster's commands
45  *
46  * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
47  * @version     1.0
48  * @since       2016-06-16
49  */
50 public class IoTSlave {
51
52         /**
53          * IoTSlave class properties
54          */
55         private Message sIoTMasterMsg;
56         private String sIoTMasterHostAdd;
57         private String sMainObjectName;
58         private int iComPort;
59         private int iRMIRegPort;
60         private int iRMIStubPort;
61         private String strFieldName;
62         private Class<?> clsMain;
63         private Object objMainCls;
64         private Object iRelFirstObject;
65         private Object iRelSecondObject;
66         private Socket socket;
67         private ObjectOutputStream outStream;
68         private ObjectInputStream inStream;
69         private Map<String,Object> mapObjNameStub;
70
71         /**
72          * IoTSet object, e.g. IoTSet<ProximitySensor> proximity_sensors;
73          * IoTRelation object, e.g. IoTRelation<ProximitySensor, LightBulb> ps_lb_relation;
74          */
75         private ISet<Object> isetObject;
76         private IoTSet<Object> iotsetObject;
77         private IRelation<Object,Object> irelObject;
78         private IoTRelation<Object,Object> iotrelObject;
79
80         // Constants that are to be extracted from config file
81         private static String STR_JAR_FILE_PATH;
82         private static String STR_OBJ_CLS_PFX;
83         private static String STR_INTERFACE_PFX;
84         private static String SKEL_CLASS_SUFFIX;
85         private static String STUB_CLASS_SUFFIX;
86         private static boolean BOOL_VERBOSE;
87         private static boolean CAPAB_BASED_RMI;
88
89         /**
90          * IoTSlave class constants - not to be changed by users
91          */
92         private static final String STR_IOT_SLAVE_NAME = "IoTSlave";
93         private static final String STR_CFG_FILE_EXT = ".config";
94         private static final String STR_CLS_FILE_EXT = ".class";
95         private static final String STR_JAR_FILE_EXT = ".jar";
96         private static final String STR_ZIP_FILE_EXT = ".zip";
97         private static final String STR_UNZIP_DIR = "./";
98         private static final Class<?>[] STR_URL_PARAM = new Class[] {URL.class };
99         private static final String STR_YES = "Yes";
100         private static final String STR_NO = "No";
101
102         /**
103          * Class constructor
104          *
105          */
106         public IoTSlave(String[] argInp) {
107
108                 sIoTMasterMsg = null;
109                 sIoTMasterHostAdd = argInp[0];
110                 iComPort = Integer.parseInt(argInp[1]);
111                 iRMIRegPort = Integer.parseInt(argInp[2]);
112                 iRMIStubPort = Integer.parseInt(argInp[3]);
113                 sMainObjectName = null;
114                 strFieldName = null;
115                 clsMain = null;
116                 objMainCls = null;
117                 isetObject = null;
118                 iotsetObject = null;
119                 irelObject = null;
120                 iotrelObject = null;
121                 iRelFirstObject = null;
122                 iRelSecondObject = null;
123                 socket = null;
124                 outStream = null;
125                 inStream = null;
126                 mapObjNameStub = new HashMap<String,Object>();
127
128                 STR_JAR_FILE_PATH = null;
129                 STR_OBJ_CLS_PFX = null;
130                 STR_INTERFACE_PFX = null;
131                 SKEL_CLASS_SUFFIX = null;
132                 STUB_CLASS_SUFFIX = null;
133                 BOOL_VERBOSE = false;
134                 CAPAB_BASED_RMI = false;
135         }
136
137         /**
138          * A method to initialize constants from config file
139          *
140          * @return void
141          */
142         private void parseIoTSlaveConfigFile() {
143                 // Parse configuration file
144                 Properties prop = new Properties();
145                 String strCfgFileName = STR_IOT_SLAVE_NAME + STR_CFG_FILE_EXT;
146                 File file = new File(strCfgFileName);
147                 try {
148                         FileInputStream fis = new FileInputStream(file);
149                         prop.load(fis);
150                 } catch (IOException ex) {
151                         System.out.println("IoTMaster: Error reading config file: " + strCfgFileName);
152                         ex.printStackTrace();
153                 }
154                 System.out.println("IoTMaster: Extracting information from config file: " + strCfgFileName);
155                 // Initialize constants from config file
156                 STR_JAR_FILE_PATH = prop.getProperty("JAR_FILE_PATH");
157                 STR_OBJ_CLS_PFX = prop.getProperty("OBJECT_CLASS_PREFIX");
158                 STR_INTERFACE_PFX = prop.getProperty("INTERFACE_PREFIX");
159                 SKEL_CLASS_SUFFIX = prop.getProperty("SKEL_CLASS_SUFFIX");
160                 STUB_CLASS_SUFFIX = prop.getProperty("STUB_CLASS_SUFFIX");
161                 if (prop.getProperty("VERBOSE").equals(STR_YES)) {
162                         BOOL_VERBOSE = true;
163                 }
164                 if (prop.getProperty("CAPAB_BASED_RMI").equals(STR_YES)) {
165                         CAPAB_BASED_RMI = true;
166                 }
167
168                 System.out.println("JAR_FILE_PATH=" + STR_JAR_FILE_PATH);
169                 System.out.println("OBJECT_CLASS_PREFIX=" + STR_OBJ_CLS_PFX);
170                 System.out.println("INTERFACE_PREFIX=" + STR_INTERFACE_PFX);
171                 System.out.println("SKEL_CLASS_SUFFIX=" + SKEL_CLASS_SUFFIX);
172                 System.out.println("STUB_CLASS_SUFFIX=" + STUB_CLASS_SUFFIX);
173                 System.out.println("CAPAB_BASED_RMI=" + CAPAB_BASED_RMI);
174                 System.out.println("IoTMaster: Information extracted successfully!");
175         }
176
177         /**
178          * Adds the content pointed by the URL to the classpath dynamically at runtime (hack!!!)
179          *
180          * @param  url         the URL pointing to the content to be added
181          * @throws IOException
182          * @see    <a href="http://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime</a>
183          */
184         private static void addURL(URL url) throws IOException {
185
186                 URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
187                 Class<?> sysclass = URLClassLoader.class;
188
189                 try {
190
191                         Method method = sysclass.getDeclaredMethod("addURL", STR_URL_PARAM);
192                         method.setAccessible(true);
193                         method.invoke(sysloader,new Object[] { url });
194
195                 } catch (Throwable t) {
196
197                         t.printStackTrace();
198                         throw new IOException("IoTSlave: Could not add URL to system classloader!");
199                 }
200         }
201
202         /**
203          * A private method to create object
204          *
205          * @return  void
206          */
207         private void createCapabBasedRMIJava(MessageCreateObject sMessage) throws 
208                 ClassNotFoundException, NoSuchMethodException, UnknownHostException {
209
210                 // Instantiate the skeleton and put in the object
211                 String strObjSkelName = STR_OBJ_CLS_PFX + "." + sMessage.getObjectClass() +
212                                                                         "." + sMessage.getObjectInterfaceName() + SKEL_CLASS_SUFFIX;
213                 RuntimeOutput.print("IoTSlave: Skeleton object: " + strObjSkelName, BOOL_VERBOSE);
214                 Class<?> clsSkel = Class.forName(strObjSkelName);
215                 Class<?> clsInt = Class.forName(STR_OBJ_CLS_PFX + "." + STR_INTERFACE_PFX + 
216                         "." + sMessage.getObjectInterfaceName());
217                 Class[] clsSkelParams = { clsInt, String.class, int.class };    // Port number is integer
218                 Constructor<?> objSkelCons = clsSkel.getDeclaredConstructor(clsSkelParams);
219                 String callbackAddress = InetAddress.getLocalHost().getHostAddress();   // Callback address is this machine's address
220                 Object objSkelParams[] = { objMainCls, callbackAddress, iRMIStubPort };
221                 // Create a new thread for each skeleton
222                 Thread objectThread = new Thread(new Runnable() {
223                         public void run() {
224                                 try {
225                                         Object objSkel = objSkelCons.newInstance(objSkelParams);
226                                 } catch (InstantiationException |
227                                                  IllegalAccessException |
228                                                  InvocationTargetException ex) {
229                                         ex.printStackTrace();
230                                 }
231                         }
232                 });
233                 objectThread.start();
234                 RuntimeOutput.print("IoTSlave: Done generating object!", BOOL_VERBOSE);
235         }
236
237         /**
238          * A private method to create object
239          *
240          * @return  void
241          */
242         private void createObject() throws IOException,
243                 ClassNotFoundException, NoSuchMethodException, InstantiationException,
244                         RemoteException, AlreadyBoundException, IllegalAccessException,
245                                 InvocationTargetException {
246
247                 // Translating into the actual Message class
248                 MessageCreateObject sMessage = (MessageCreateObject) sIoTMasterMsg;
249                 // Instantiate object using reflection
250                 String strObjClassName = STR_OBJ_CLS_PFX + "." + sMessage.getObjectClass() +
251                                                                                                                  "." + sMessage.getObjectClass();
252                 File file = new File(STR_JAR_FILE_PATH + sMessage.getObjectClass() + STR_JAR_FILE_EXT);
253                 RuntimeOutput.print("IoTSlave: DEBUG print path: " + STR_JAR_FILE_PATH +
254                                                                                          sMessage.getObjectClass() + STR_JAR_FILE_EXT, BOOL_VERBOSE);
255                 addURL(file.toURI().toURL());
256                 clsMain = Class.forName(strObjClassName);
257                 Class[] clsParams = sMessage.getObjectFldCls();
258                 Constructor<?> ct = clsMain.getDeclaredConstructor(clsParams);
259                 Object objParams[] = sMessage.getObjectFields();
260                 objMainCls = ct.newInstance(objParams);
261                 RuntimeOutput.print("IoTSlave: Creating RMI skeleton: " +
262                         sMessage.getHostAddress() + ":" + sMessage.getRMIRegPort() +
263                         " with RMI stub port: " + iRMIStubPort, BOOL_VERBOSE);
264                 if (CAPAB_BASED_RMI) {
265                 // Use the new capability-based RMI in Java
266                         createCapabBasedRMIJava(sMessage);
267                 } else {
268                         // Register object to RMI - there are 2 ports: RMI registry port and RMI stub port
269                         Object objStub = (Object)
270                                 UnicastRemoteObject.exportObject((Remote) objMainCls, iRMIStubPort);
271                         Registry registry = LocateRegistry.createRegistry(iRMIRegPort);
272                         registry.bind(sMessage.getObjectName(), (Remote) objStub);
273                 }
274                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
275                 RuntimeOutput.print("IoTSlave: Registering object via RMI!", BOOL_VERBOSE);
276
277         }
278
279         
280         /**
281          * A private method to transfer file
282          *
283          * @return  void
284          */
285         private void transferFile() throws IOException,
286                 UnknownHostException, FileNotFoundException {
287
288                 // Translating into the actual Message class
289                 MessageSendFile sMessage = (MessageSendFile) sIoTMasterMsg;
290
291                 // Send back the received message as acknowledgement
292                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
293
294                 // Write file to the current location
295                 Socket filesocket = new Socket(sIoTMasterHostAdd, iComPort);
296                 InputStream inFileStream = filesocket.getInputStream();
297                 OutputStream outFileStream = new FileOutputStream(sMessage.getFileName());
298                 byte[] bytFile = new byte[Math.toIntExact(sMessage.getFileSize())];
299
300                 int iCount = 0;
301                 while ((iCount = inFileStream.read(bytFile)) > 0) {
302                         outFileStream.write(bytFile, 0, iCount);
303                 }
304                 // Unzip if this is a zipped file
305                 if (sMessage.getFileName().contains(STR_ZIP_FILE_EXT)) {
306                         RuntimeOutput.print("IoTSlave: Unzipping file: " + sMessage.getFileName(), BOOL_VERBOSE);
307                         try {
308                                 ZipFile zipFile = new ZipFile(sMessage.getFileName());
309                                 zipFile.extractAll(STR_UNZIP_DIR);
310                         } catch (ZipException ex) {
311                                 System.out.println("IoTSlave: Error in unzipping file!");
312                                 ex.printStackTrace();
313                         }
314                 }
315                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
316                 RuntimeOutput.print("IoTSlave: Receiving file transfer!", BOOL_VERBOSE);
317         }
318
319         /**
320          * A private method to create a main object
321          *
322          * @return  void
323          */
324         private void createMainObject() throws IOException,
325                 ClassNotFoundException, InstantiationException, IllegalAccessException,
326                         InvocationTargetException {
327
328                 // Translating into the actual Message class
329                 MessageCreateMainObject sMessage = (MessageCreateMainObject) sIoTMasterMsg;
330
331                 // Getting controller class
332                 File file = new File(STR_JAR_FILE_PATH + sMessage.getObjectName() + STR_JAR_FILE_EXT);
333                 RuntimeOutput.print("IoTSlave: DEBUG print path: " + STR_JAR_FILE_PATH +
334                                                                                          sMessage.getObjectName() + STR_JAR_FILE_EXT, BOOL_VERBOSE);
335                 addURL(file.toURI().toURL());
336                 // We will always have a package name <object name>.<object name>
337                 // e.g. SmartLightsController.SmartLightsController
338                 sMainObjectName = sMessage.getObjectName();
339                 clsMain = Class.forName(sMainObjectName + "." + sMainObjectName);
340                 objMainCls = clsMain.newInstance();
341
342                 // Send back the received message as acknowledgement
343                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
344                 RuntimeOutput.print("IoTSlave: Instantiating main controller/device class "
345                                                                                          + sMessage.getObjectName(), BOOL_VERBOSE);
346
347         }
348
349         /**
350          * A private method to create a new IoTSet
351          *
352          * @return  void
353          */
354         private void createNewIoTSet() throws IOException {
355
356                 // Translating into the actual Message class
357                 MessageCreateSetRelation sMessage = (MessageCreateSetRelation) sIoTMasterMsg;
358
359                 // Initialize field name
360                 strFieldName = sMessage.getObjectFieldName();
361                 RuntimeOutput.print("IoTSlave: Setting up field " + strFieldName, BOOL_VERBOSE);
362
363                 // Creating a new IoTSet object
364                 isetObject = new ISet<Object>();
365
366                 // Send back the received message as acknowledgement
367                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
368                 RuntimeOutput.print("IoTSlave: Creating a new IoTSet object!", BOOL_VERBOSE);
369
370         }
371
372         /**
373          * A private method to create a new IoTRelation
374          *
375          * @return  void
376          */
377         private void createNewIoTRelation() throws IOException {
378
379                 // Translating into the actual Message class
380                 MessageCreateSetRelation sMessage = (MessageCreateSetRelation) sIoTMasterMsg;
381
382                 // Initialize field name
383                 strFieldName = sMessage.getObjectFieldName();
384                 RuntimeOutput.print("IoTSlave: Setting up field " + strFieldName, BOOL_VERBOSE);
385
386                 // Creating a new IoTRelation object
387                 irelObject = new IRelation<Object,Object>();
388
389                 // Send back the received message as acknowledgement
390                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
391                 RuntimeOutput.print("IoTSlave: Creating a new IoTRelation object!", BOOL_VERBOSE);
392
393         }
394
395         /**
396          * A private method to get an object from the registry
397          *
398          * @return  Object
399          */
400         private Object getObjectFromRegistry() throws RemoteException,
401                         ClassNotFoundException, NotBoundException {
402
403                 // Translating into the actual Message class
404                 MessageGetObject sMessage = (MessageGetObject) sIoTMasterMsg;
405
406                 // Locate RMI registry and add object into IoTSet
407                 Registry registry =
408                         LocateRegistry.getRegistry(sMessage.getHostAddress(), sMessage.getRMIRegPort());
409                 RuntimeOutput.print("IoTSlave: Looking for RMI registry: " +
410                         sMessage.getHostAddress() + ":" + sMessage.getRMIRegPort() +
411                         " with RMI stub port: " + sMessage.getRMIStubPort(), BOOL_VERBOSE);
412                 Object stubObj = registry.lookup(sMessage.getObjectName());
413                 RuntimeOutput.print("IoTSlave: Looking for object name: " + sMessage.getObjectName(), BOOL_VERBOSE);
414
415                 // Class conversion to interface class of this class,
416                 // e.g. ProximitySensorImpl has ProximitySensor interface
417                 String strObjClassInterfaceName = STR_OBJ_CLS_PFX + "." + STR_INTERFACE_PFX + "." +
418                         sMessage.getObjectInterfaceName();
419                 Class<?> clsInf = Class.forName(strObjClassInterfaceName);
420                 Object stubObjConv = clsInf.cast(stubObj);
421
422                 return stubObjConv;
423         }
424
425         /**
426          * A private method to get an object and create a stub
427          * <p>
428          * This is using the capability-based RMI skeleton and stub scheme
429          *
430          * @return  Object
431          */
432         private Object getObjectFromStub() throws RemoteException,
433                         ClassNotFoundException, NoSuchMethodException, InstantiationException, 
434                         IllegalAccessException, NotBoundException, InvocationTargetException, UnknownHostException {
435
436                 // Translating into the actual Message class
437                 MessageGetObject sMessage = (MessageGetObject) sIoTMasterMsg;
438                 Object stubObjConv = null;
439                 String strObjectName = sMessage.getObjectName();
440                 String strObjClassInterfaceName = STR_OBJ_CLS_PFX + "." + STR_INTERFACE_PFX + "." +
441                         sMessage.getObjectStubInterfaceName();
442                 Class<?> clsInf = Class.forName(strObjClassInterfaceName);
443                 if (mapObjNameStub.containsKey(strObjectName)) {
444                         RuntimeOutput.print("IoTSlave: Getting back object on slave: " + strObjectName, BOOL_VERBOSE);
445                         stubObjConv = clsInf.cast(mapObjNameStub.get(strObjectName));
446                 } else {
447                         // Instantiate the stub and put in the object
448                         String strObjStubName = sMainObjectName + "." + sMessage.getObjectStubInterfaceName() + STUB_CLASS_SUFFIX;
449                         Class<?> clsStub = Class.forName(strObjStubName);       // Port number is integer
450                         Class[] clsStubParams = { int.class, String.class, String.class, int.class, int[].class };
451                         Constructor<?> objStubCons = clsStub.getDeclaredConstructor(clsStubParams);
452                         Integer[] portsInteger = sMessage.getRMICallbackPorts();
453                         int[] ports = Arrays.stream(portsInteger).mapToInt(Integer::intValue).toArray();
454
455                         int rev = 0;
456                         String callbackAddress = InetAddress.getLocalHost().getHostAddress();   // Callback address is this machine's address
457                         Object objStubParams[] = { sMessage.getRMIStubPort(), sMessage.getHostAddress(), callbackAddress,
458                                                                                 rev, ports };
459                         RuntimeOutput.print("IoTSlave: Creating RMI stub: " +
460                                 sMessage.getHostAddress() + ":" + sMessage.getRMIRegPort() +
461                                 " with callback address: " + callbackAddress + " and RMI stub port: " + sMessage.getRMIStubPort(), BOOL_VERBOSE);
462                         Object stubObj = objStubCons.newInstance(objStubParams);
463                         // Class conversion to interface class of this class,
464                         // e.g. ProximitySensorImpl has ProximitySensor interface
465                         RuntimeOutput.print("IoTSlave: Registering new stub object: " + strObjectName, BOOL_VERBOSE);
466                         mapObjNameStub.put(strObjectName, stubObj);
467                         stubObjConv = clsInf.cast(stubObj);
468                 }
469
470                 return stubObjConv;
471         }
472
473         /**
474          * A private method to get an IoTSet object
475          *
476          * @return  void
477          */
478         private void getIoTSetObject() throws IOException,
479                 ClassNotFoundException, RemoteException, NotBoundException, NoSuchMethodException,
480                 InstantiationException, IllegalAccessException, InvocationTargetException {
481
482                 Object objRegistry = null;
483                 if (CAPAB_BASED_RMI)
484                         objRegistry = getObjectFromStub();
485                 else
486                         objRegistry = getObjectFromRegistry();
487                 isetObject.add(objRegistry);
488                 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
489
490                 // Send back the received message as acknowledgement
491                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
492                 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
493         }
494
495         /**
496          * A private method to get an IoTRelation first object
497          *
498          * @return  void
499          */
500         private void getIoTRelationFirstObject() throws IOException,
501                 ClassNotFoundException, RemoteException, NotBoundException, NoSuchMethodException,
502                 InstantiationException, IllegalAccessException, InvocationTargetException {
503
504                 Object objRegistry = null;
505                 if (CAPAB_BASED_RMI)
506                         objRegistry = getObjectFromStub();
507                 else
508                         objRegistry = getObjectFromRegistry();
509                 iRelFirstObject = objRegistry;
510
511                 // Send back the received message as acknowledgement
512                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
513                 RuntimeOutput.print("IoTSlave: Getting a first object for IoTRelation!", BOOL_VERBOSE);
514
515         }
516
517         /**
518          * A private method to get an IoTRelation second object
519          *
520          * @return  void
521          */
522         private void getIoTRelationSecondObject() throws IOException,
523                 ClassNotFoundException, RemoteException, NotBoundException, NoSuchMethodException,
524                 InstantiationException, IllegalAccessException, InvocationTargetException {
525
526                 Object objRegistry = null;
527                 if (CAPAB_BASED_RMI)
528                         objRegistry = getObjectFromStub();
529                 else
530                         objRegistry = getObjectFromRegistry();
531                 iRelSecondObject = objRegistry;
532
533                 // Now add the first and the second object into IoTRelation
534                 irelObject.put(iRelFirstObject, iRelSecondObject);
535                 RuntimeOutput.print("IoTSlave: This IoTRelation now has: " + irelObject.size() + " entry(s)", BOOL_VERBOSE);
536
537                 // Send back the received message as acknowledgement
538                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
539                 RuntimeOutput.print("IoTSlave: Getting a second object for IoTRelation!", BOOL_VERBOSE);
540
541         }
542
543         /**
544          * A private method to reinitialize IoTSet field
545          *
546          * @return  void
547          */
548         private void reinitializeIoTSetField() throws IOException,
549                 IllegalAccessException, NoSuchFieldException {
550
551                 // Reinitialize IoTSet field after getting all the objects
552                 iotsetObject = new IoTSet<Object>(isetObject.values());
553
554                 // Private fields need getDeclaredField(), while public fields use getField()
555                 Field fld = clsMain.getDeclaredField(strFieldName);
556                 boolean bAccess = fld.isAccessible();
557                 fld.setAccessible(true);
558                 fld.set(objMainCls, iotsetObject);
559                 fld.setAccessible(bAccess);
560                 RuntimeOutput.print("IoTSlave: Reinitializing field " + strFieldName, BOOL_VERBOSE);
561
562                 // Send back the received message as acknowledgement
563                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
564                 RuntimeOutput.print("IoTSlave: Reinitializing IoTSet field!", BOOL_VERBOSE);
565
566         }
567
568         /**
569          * A private method to reinitialize IoTRelation field
570          *
571          * @return  void
572          */
573         private void reinitializeIoTRelationField() throws IOException,
574                 IllegalAccessException, NoSuchFieldException {
575
576                 // Reinitialize IoTSet field after getting all the objects
577                 iotrelObject = new IoTRelation<Object,Object>(irelObject.relationMap(), irelObject.size());
578
579                 // Private fields need getDeclaredField(), while public fields use getField()
580                 Field fld = clsMain.getDeclaredField(strFieldName);
581                 boolean bAccess = fld.isAccessible();
582                 fld.setAccessible(true);
583                 fld.set(objMainCls, iotrelObject);
584                 fld.setAccessible(bAccess);
585                 RuntimeOutput.print("IoTSlave: Reinitializing field " + strFieldName, BOOL_VERBOSE);
586
587                 // Send back the received message as acknowledgement
588                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
589                 RuntimeOutput.print("IoTSlave: Reinitializing IoTRelation field!", BOOL_VERBOSE);
590
591         }
592
593         /**
594          * A private method to get the device driver object's IoTSet
595          * <p>
596          * This is to handle device driver's IoTSet that contains IP addresses
597          *
598          * @return  void
599          */
600         private void getDeviceIoTSetObject() throws IOException {
601
602                 // Translating into the actual Message class
603                 MessageGetDeviceObject sMessage = (MessageGetDeviceObject) sIoTMasterMsg;
604
605                 // Get IoTSet objects for IP address set on device driver/controller
606                 IoTDeviceAddress objDeviceAddress = new IoTDeviceAddress(sMessage.getHostAddress(),
607                         sMessage.getSourceDeviceDriverPort(),
608                         sMessage.getDestinationDeviceDriverPort(),
609                         sMessage.isSourcePortWildCard(),
610                         sMessage.isDestinationPortWildCard());
611                 RuntimeOutput.print("IoTSlave: Device address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
612                 isetObject.add(objDeviceAddress);
613                 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
614
615                 // Send back the received message as acknowledgement
616                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
617                 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
618
619         }
620
621         /**
622          * A private method to get the device driver object's IoTSet for IoTZigbeeAddress
623          * <p>
624          * This is to handle device driver's IoTSet that contains Zigbee addresses
625          *
626          * @return  void
627          */
628         private void getZBDevIoTSetObject() throws IOException {
629
630                 // Translating into the actual Message class
631                 MessageGetSimpleDeviceObject sMessage = (MessageGetSimpleDeviceObject) sIoTMasterMsg;
632
633                 // Get IoTSet objects for IP address set on device driver/controller
634                 IoTZigbeeAddress objZBDevAddress = new IoTZigbeeAddress(sMessage.getHostAddress());
635                 RuntimeOutput.print("IoTSlave: Device address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
636                 isetObject.add(objZBDevAddress);
637                 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
638
639                 // Send back the received message as acknowledgement
640                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
641                 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
642
643         }
644
645         
646         /**
647          * A private method to get IoTAddress objects for IoTSet
648          *
649          * @return  void
650          */
651         private void getAddIoTSetObject() throws IOException {
652
653                 // Translating into the actual Message class
654                 MessageGetSimpleDeviceObject sMessage = (MessageGetSimpleDeviceObject) sIoTMasterMsg;
655
656                 // Get IoTSet objects for IP address set on device driver/controller
657                 IoTAddress objAddress = new IoTAddress(sMessage.getHostAddress());
658                 RuntimeOutput.print("IoTSlave: Address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
659                 isetObject.add(objAddress);
660                 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
661                 // Send back the received message as acknowledgement
662                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
663                 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
664
665         }
666         
667         /**
668          * A private method to invoke init() method in the controller object
669          *
670          * @return  void
671          */
672         private void invokeInitMethod() throws IOException {
673
674                 new Thread() {
675                         public void run() {
676                                 try {
677                                         Class<?> noparams[] = {};
678                                         Method method = clsMain.getDeclaredMethod("init", noparams);
679                                         method.invoke(objMainCls);
680                                 } catch (NoSuchMethodException  |
681                                                  IllegalAccessException |
682                                                  InvocationTargetException ex) {
683                                         System.out.println("IoTSlave: Exception: "
684                                                  + ex.getMessage());
685                                         ex.printStackTrace();
686                                 }
687                         }
688                 }.start();
689
690                 // Start a new thread to invoke the init function
691                 RuntimeOutput.print("IoTSlave: Invoke init method! Job done!", BOOL_VERBOSE);
692
693                 // Send back the received message as acknowledgement
694                 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
695
696         }
697
698         /**
699          * A public method to do communication with IoTMaster
700          *
701          * @params  iIndex  Integer index
702          * @return  void
703          */
704         public void commIoTMaster() {
705
706                 try {
707
708                         // Loop, receive and process commands from IoTMaster
709                         socket = new Socket(sIoTMasterHostAdd, iComPort);
710                         outStream = new ObjectOutputStream(socket.getOutputStream());
711                         inStream = new ObjectInputStream(socket.getInputStream());
712
713                         LOOP:
714                         while(true) {
715                                 // Get the first payload
716                                 RuntimeOutput.print("IoTSlave: Slave waiting...", BOOL_VERBOSE);
717                                 sIoTMasterMsg = (Message) inStream.readObject();
718
719                                 // Check payload message from IoTMaster and make a decision
720                                 switch (sIoTMasterMsg.getMessage()) {
721
722                                 case CREATE_OBJECT:
723                                         createObject();
724                                         break;
725
726                                 case TRANSFER_FILE:
727                                         transferFile();
728                                         break;
729
730                                 case CREATE_MAIN_OBJECT:
731                                         createMainObject();
732                                         break;
733
734                                 case CREATE_NEW_IOTSET:
735                                         createNewIoTSet();
736                                         break;
737
738                                 case CREATE_NEW_IOTRELATION:
739                                         createNewIoTRelation();
740                                         break;
741
742                                 case GET_IOTSET_OBJECT:
743                                         getIoTSetObject();
744                                         break;
745
746                                 case GET_IOTRELATION_FIRST_OBJECT:
747                                         getIoTRelationFirstObject();
748                                         break;
749
750                                 case GET_IOTRELATION_SECOND_OBJECT:
751                                         getIoTRelationSecondObject();
752                                         break;
753
754                                 case REINITIALIZE_IOTSET_FIELD:
755                                         reinitializeIoTSetField();
756                                         break;
757
758                                 case REINITIALIZE_IOTRELATION_FIELD:
759                                         reinitializeIoTRelationField();
760                                         break;
761
762                                 case GET_DEVICE_IOTSET_OBJECT:
763                                         getDeviceIoTSetObject();
764                                         break;
765
766                                 case GET_ZB_DEV_IOTSET_OBJECT:
767                                         getZBDevIoTSetObject();
768                                         break;
769
770                                 case GET_ADD_IOTSET_OBJECT:
771                                         getAddIoTSetObject();
772                                         break;
773
774                                 case INVOKE_INIT_METHOD:
775                                         invokeInitMethod();
776                                         break;
777
778                                 case END_SESSION:
779                                         // END of session
780                                         break LOOP;
781
782                                 default:
783                                         break;
784                                 }
785                         }
786                         RuntimeOutput.print("IoTSlave: Session ends!", BOOL_VERBOSE);
787
788                         // Closing streams and end session
789                         outStream.close();
790                         inStream.close();
791                         socket.close();
792                         RuntimeOutput.print("IoTSlave: Closing!", BOOL_VERBOSE);
793                         // We have to continuously loop because we are preserving our stubs and skeletons
794                         //while(true) { }
795
796                 } catch (IOException               |
797                                  ClassNotFoundException    |
798                                  NoSuchMethodException     |
799                                  InstantiationException    |
800                                  AlreadyBoundException     |
801                                  IllegalAccessException    |
802                                  InvocationTargetException |
803                                  NotBoundException         |
804                                  NoSuchFieldException ex) {
805                         System.out.println("IoTSlave: Exception: "
806                                  + ex.getMessage());
807                         ex.printStackTrace();
808                 }
809         }
810
811         public static void main(String args[]) {
812                 IoTSlave iotSlave = new IoTSlave(args);
813                 iotSlave.parseIoTSlaveConfigFile();
814                 iotSlave.commIoTMaster();
815         }
816 }