1 package iotruntime.slave;
4 import iotruntime.zigbee.*;
5 import iotruntime.messages.*;
6 import iotruntime.master.RuntimeOutput;
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.Socket;
23 import java.net.UnknownHostException;
25 import java.net.URLClassLoader;
26 import java.rmi.registry.LocateRegistry;
27 import java.rmi.registry.Registry;
28 import java.rmi.Remote;
29 import java.rmi.RemoteException;
30 import java.rmi.AlreadyBoundException;
31 import java.rmi.NotBoundException;
32 import java.rmi.server.UnicastRemoteObject;
33 import java.util.Properties;
36 import net.lingala.zip4j.exception.ZipException;
37 import net.lingala.zip4j.core.ZipFile;
39 /** Class IoTSlave is run by IoTMaster on a different JVM's.
40 * It needs to respond to IoTMaster's commands
42 * @author Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
46 public class IoTSlave {
49 * IoTSlave class properties
51 private Message sIoTMasterMsg;
52 private String sIoTMasterHostAdd;
54 private int iRMIRegPort;
55 private int iRMIStubPort;
56 private String strFieldName;
57 private Class<?> clsMain;
58 private Object objMainCls;
59 private Object iRelFirstObject;
60 private Object iRelSecondObject;
61 private Socket socket;
62 private ObjectOutputStream outStream;
63 private ObjectInputStream inStream;
65 * IoTSet object, e.g. IoTSet<ProximitySensor> proximity_sensors;
66 * IoTRelation object, e.g. IoTRelation<ProximitySensor, LightBulb> ps_lb_relation;
68 private ISet<Object> isetObject;
69 private IoTSet<Object> iotsetObject;
70 private IRelation<Object,Object> irelObject;
71 private IoTRelation<Object,Object> iotrelObject;
73 // Constants that are to be extracted from config file
74 private static String STR_JAR_FILE_PATH;
75 private static String STR_OBJ_CLS_PFX;
76 private static String STR_INTERFACE_PFX;
77 private static boolean BOOL_VERBOSE;
80 * IoTSlave class constants - not to be changed by users
82 private static final String STR_IOT_SLAVE_NAME = "IoTSlave";
83 private static final String STR_CFG_FILE_EXT = ".config";
84 private static final String STR_CLS_FILE_EXT = ".class";
85 private static final String STR_JAR_FILE_EXT = ".jar";
86 private static final String STR_ZIP_FILE_EXT = ".zip";
87 private static final String STR_UNZIP_DIR = "./";
88 private static final Class<?>[] STR_URL_PARAM = new Class[] {URL.class };
89 private static final String STR_YES = "Yes";
90 private static final String STR_NO = "No";
96 public IoTSlave(String[] argInp) {
99 sIoTMasterHostAdd = argInp[0];
100 iComPort = Integer.parseInt(argInp[1]);
101 iRMIRegPort = Integer.parseInt(argInp[2]);
102 iRMIStubPort = Integer.parseInt(argInp[3]);
110 iRelFirstObject = null;
111 iRelSecondObject = null;
116 STR_JAR_FILE_PATH = null;
117 STR_OBJ_CLS_PFX = null;
118 STR_INTERFACE_PFX = null;
119 BOOL_VERBOSE = false;
123 * A method to initialize constants from config file
127 private void parseIoTSlaveConfigFile() {
128 // Parse configuration file
129 Properties prop = new Properties();
130 String strCfgFileName = STR_IOT_SLAVE_NAME + STR_CFG_FILE_EXT;
131 File file = new File(strCfgFileName);
133 FileInputStream fis = new FileInputStream(file);
135 } catch (IOException ex) {
136 System.out.println("IoTMaster: Error reading config file: " + strCfgFileName);
137 ex.printStackTrace();
139 System.out.println("IoTMaster: Extracting information from config file: " + strCfgFileName);
140 // Initialize constants from config file
141 STR_JAR_FILE_PATH = prop.getProperty("JAR_FILE_PATH");
142 STR_OBJ_CLS_PFX = prop.getProperty("OBJECT_CLASS_PREFIX");
143 STR_INTERFACE_PFX = prop.getProperty("INTERFACE_PREFIX");
144 STR_INTERFACE_PFX = prop.getProperty("INTERFACE_PREFIX");
145 if (prop.getProperty("VERBOSE").equals(STR_YES)) {
149 System.out.println("JAR_FILE_PATH=" + STR_JAR_FILE_PATH);
150 System.out.println("OBJECT_CLASS_PREFIX=" + STR_OBJ_CLS_PFX);
151 System.out.println("INTERFACE_PREFIX=" + STR_INTERFACE_PFX);
152 System.out.println("IoTMaster: Information extracted successfully!");
156 * Adds the content pointed by the URL to the classpath dynamically at runtime (hack!!!)
158 * @param url the URL pointing to the content to be added
159 * @throws IOException
160 * @see <a href="http://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime</a>
162 private static void addURL(URL url) throws IOException {
164 URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
165 Class<?> sysclass = URLClassLoader.class;
169 Method method = sysclass.getDeclaredMethod("addURL", STR_URL_PARAM);
170 method.setAccessible(true);
171 method.invoke(sysloader,new Object[] { url });
173 } catch (Throwable t) {
176 throw new IOException("IoTSlave: Could not add URL to system classloader!");
181 * A private method to create object
185 private void createObject() throws IOException,
186 ClassNotFoundException, NoSuchMethodException, InstantiationException,
187 RemoteException, AlreadyBoundException, IllegalAccessException,
188 InvocationTargetException {
190 // Translating into the actual Message class
191 MessageCreateObject sMessage = (MessageCreateObject) sIoTMasterMsg;
193 // Instantiate object using reflection
194 String strObjClassName = STR_OBJ_CLS_PFX + "." + sMessage.getObjectClass() +
195 "." + sMessage.getObjectClass();
196 File file = new File(STR_JAR_FILE_PATH + sMessage.getObjectClass() + STR_JAR_FILE_EXT);
197 RuntimeOutput.print("IoTSlave: DEBUG print path: " + STR_JAR_FILE_PATH +
198 sMessage.getObjectClass() + STR_JAR_FILE_EXT, BOOL_VERBOSE);
199 addURL(file.toURI().toURL());
200 clsMain = Class.forName(strObjClassName);
202 Class[] clsParams = sMessage.getObjectFldCls();
203 Constructor<?> ct = clsMain.getDeclaredConstructor(clsParams);
204 Object objParams[] = sMessage.getObjectFields();
205 objMainCls = ct.newInstance(objParams);
206 RuntimeOutput.print("IoTSlave: Create object!", BOOL_VERBOSE);
208 // Register object to RMI - there are 2 ports: RMI registry port and RMI stub port
209 Object objStub = (Object)
210 UnicastRemoteObject.exportObject((Remote) objMainCls, iRMIStubPort);
211 Registry registry = LocateRegistry.createRegistry(iRMIRegPort);
212 registry.bind(sMessage.getObjectName(), (Remote) objStub);
213 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
214 RuntimeOutput.print("IoTSlave: Registering object via RMI!", BOOL_VERBOSE);
220 * A private method to transfer file
224 private void transferFile() throws IOException,
225 UnknownHostException, FileNotFoundException {
227 // Translating into the actual Message class
228 MessageSendFile sMessage = (MessageSendFile) sIoTMasterMsg;
230 // Send back the received message as acknowledgement
231 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
233 // Write file to the current location
234 Socket filesocket = new Socket(sIoTMasterHostAdd, iComPort);
235 InputStream inFileStream = filesocket.getInputStream();
236 OutputStream outFileStream = new FileOutputStream(sMessage.getFileName());
237 byte[] bytFile = new byte[Math.toIntExact(sMessage.getFileSize())];
240 while ((iCount = inFileStream.read(bytFile)) > 0) {
241 outFileStream.write(bytFile, 0, iCount);
243 // Unzip if this is a zipped file
244 if (sMessage.getFileName().contains(STR_ZIP_FILE_EXT)) {
245 RuntimeOutput.print("IoTSlave: Unzipping file: " + sMessage.getFileName(), BOOL_VERBOSE);
247 ZipFile zipFile = new ZipFile(sMessage.getFileName());
248 zipFile.extractAll(STR_UNZIP_DIR);
249 } catch (ZipException ex) {
250 System.out.println("IoTSlave: Error in unzipping file!");
251 ex.printStackTrace();
254 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
255 RuntimeOutput.print("IoTSlave: Receiving file transfer!", BOOL_VERBOSE);
259 * A private method to create a main object
263 private void createMainObject() throws IOException,
264 ClassNotFoundException, InstantiationException, IllegalAccessException,
265 InvocationTargetException {
267 // Translating into the actual Message class
268 MessageCreateMainObject sMessage = (MessageCreateMainObject) sIoTMasterMsg;
270 // Getting controller class
271 File file = new File(STR_JAR_FILE_PATH + sMessage.getObjectName() + STR_JAR_FILE_EXT);
272 RuntimeOutput.print("IoTSlave: DEBUG print path: " + STR_JAR_FILE_PATH +
273 sMessage.getObjectName() + STR_JAR_FILE_EXT, BOOL_VERBOSE);
274 addURL(file.toURI().toURL());
275 // We will always have a package name <object name>.<object name>
276 // e.g. SmartLightsController.SmartLightsController
277 clsMain = Class.forName(sMessage.getObjectName() + "." + sMessage.getObjectName());
278 objMainCls = clsMain.newInstance();
280 // Send back the received message as acknowledgement
281 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
282 RuntimeOutput.print("IoTSlave: Instantiating main controller/device class "
283 + sMessage.getObjectName(), BOOL_VERBOSE);
288 * A private method to create a new IoTSet
292 private void createNewIoTSet() throws IOException {
294 // Translating into the actual Message class
295 MessageCreateSetRelation sMessage = (MessageCreateSetRelation) sIoTMasterMsg;
297 // Initialize field name
298 strFieldName = sMessage.getObjectFieldName();
299 RuntimeOutput.print("IoTSlave: Setting up field " + strFieldName, BOOL_VERBOSE);
301 // Creating a new IoTSet object
302 isetObject = new ISet<Object>();
304 // Send back the received message as acknowledgement
305 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
306 RuntimeOutput.print("IoTSlave: Creating a new IoTSet object!", BOOL_VERBOSE);
311 * A private method to create a new IoTRelation
315 private void createNewIoTRelation() throws IOException {
317 // Translating into the actual Message class
318 MessageCreateSetRelation sMessage = (MessageCreateSetRelation) sIoTMasterMsg;
320 // Initialize field name
321 strFieldName = sMessage.getObjectFieldName();
322 RuntimeOutput.print("IoTSlave: Setting up field " + strFieldName, BOOL_VERBOSE);
324 // Creating a new IoTRelation object
325 irelObject = new IRelation<Object,Object>();
327 // Send back the received message as acknowledgement
328 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
329 RuntimeOutput.print("IoTSlave: Creating a new IoTRelation object!", BOOL_VERBOSE);
334 * A private method to get an object from the registry
338 private Object getObjectFromRegistry() throws RemoteException,
339 ClassNotFoundException, NotBoundException {
341 // Translating into the actual Message class
342 MessageGetObject sMessage = (MessageGetObject) sIoTMasterMsg;
344 // Locate RMI registry and add object into IoTSet
346 LocateRegistry.getRegistry(sMessage.getHostAddress(), sMessage.getRMIRegPort());
347 RuntimeOutput.print("IoTSlave: Looking for RMI registry: " +
348 sMessage.getHostAddress() + ":" + sMessage.getRMIRegPort() +
349 " with RMI stub port: " + sMessage.getRMIStubPort(), BOOL_VERBOSE);
350 Object stubObj = registry.lookup(sMessage.getObjectName());
351 RuntimeOutput.print("IoTSlave: Looking for object name: " + sMessage.getObjectName(), BOOL_VERBOSE);
353 // Class conversion to interface class of this class,
354 // e.g. ProximitySensorImpl has ProximitySensor interface
355 String strObjClassInterfaceName = STR_OBJ_CLS_PFX + "." + STR_INTERFACE_PFX + "." +
356 sMessage.getObjectInterfaceName();
357 Class<?> clsInf = Class.forName(strObjClassInterfaceName);
358 Object stubObjConv = clsInf.cast(stubObj);
364 * A private method to get an IoTSet object
368 private void getIoTSetObject() throws IOException,
369 ClassNotFoundException, RemoteException, NotBoundException {
371 Object objRegistry = getObjectFromRegistry();
372 isetObject.add(objRegistry);
373 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
375 // Send back the received message as acknowledgement
376 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
377 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
382 * A private method to get an IoTRelation first object
386 private void getIoTRelationFirstObject() throws IOException,
387 ClassNotFoundException, RemoteException, NotBoundException {
389 Object objRegistry = getObjectFromRegistry();
390 iRelFirstObject = objRegistry;
392 // Send back the received message as acknowledgement
393 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
394 RuntimeOutput.print("IoTSlave: Getting a first object for IoTRelation!", BOOL_VERBOSE);
399 * A private method to get an IoTRelation second object
403 private void getIoTRelationSecondObject() throws IOException,
404 ClassNotFoundException, RemoteException, NotBoundException {
406 Object objRegistry = getObjectFromRegistry();
407 iRelSecondObject = objRegistry;
409 // Now add the first and the second object into IoTRelation
410 irelObject.put(iRelFirstObject, iRelSecondObject);
411 RuntimeOutput.print("IoTSlave: This IoTRelation now has: " + irelObject.size() + " entry(s)", BOOL_VERBOSE);
413 // Send back the received message as acknowledgement
414 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
415 RuntimeOutput.print("IoTSlave: Getting a second object for IoTRelation!", BOOL_VERBOSE);
420 * A private method to reinitialize IoTSet field
424 private void reinitializeIoTSetField() throws IOException,
425 IllegalAccessException, NoSuchFieldException {
427 // Reinitialize IoTSet field after getting all the objects
428 iotsetObject = new IoTSet<Object>(isetObject.values());
430 // Private fields need getDeclaredField(), while public fields use getField()
431 Field fld = clsMain.getDeclaredField(strFieldName);
432 boolean bAccess = fld.isAccessible();
433 fld.setAccessible(true);
434 fld.set(objMainCls, iotsetObject);
435 fld.setAccessible(bAccess);
436 RuntimeOutput.print("IoTSlave: Reinitializing field " + strFieldName, BOOL_VERBOSE);
438 // Send back the received message as acknowledgement
439 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
440 RuntimeOutput.print("IoTSlave: Reinitializing IoTSet field!", BOOL_VERBOSE);
445 * A private method to reinitialize IoTRelation field
449 private void reinitializeIoTRelationField() throws IOException,
450 IllegalAccessException, NoSuchFieldException {
452 // Reinitialize IoTSet field after getting all the objects
453 iotrelObject = new IoTRelation<Object,Object>(irelObject.relationMap(), irelObject.size());
455 // Private fields need getDeclaredField(), while public fields use getField()
456 Field fld = clsMain.getDeclaredField(strFieldName);
457 boolean bAccess = fld.isAccessible();
458 fld.setAccessible(true);
459 fld.set(objMainCls, iotrelObject);
460 fld.setAccessible(bAccess);
461 RuntimeOutput.print("IoTSlave: Reinitializing field " + strFieldName, BOOL_VERBOSE);
463 // Send back the received message as acknowledgement
464 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
465 RuntimeOutput.print("IoTSlave: Reinitializing IoTRelation field!", BOOL_VERBOSE);
470 * A private method to get the device driver object's IoTSet
472 * This is to handle device driver's IoTSet that contains IP addresses
476 private void getDeviceIoTSetObject() throws IOException {
478 // Translating into the actual Message class
479 MessageGetDeviceObject sMessage = (MessageGetDeviceObject) sIoTMasterMsg;
481 // Get IoTSet objects for IP address set on device driver/controller
482 IoTDeviceAddress objDeviceAddress = new IoTDeviceAddress(sMessage.getHostAddress(),
483 sMessage.getSourceDeviceDriverPort(),
484 sMessage.getDestinationDeviceDriverPort(),
485 sMessage.isSourcePortWildCard(),
486 sMessage.isDestinationPortWildCard());
487 RuntimeOutput.print("IoTSlave: Device address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
488 isetObject.add(objDeviceAddress);
489 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
491 // Send back the received message as acknowledgement
492 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
493 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
498 * A private method to get the device driver object's IoTSet for IoTZigbeeAddress
500 * This is to handle device driver's IoTSet that contains Zigbee addresses
504 private void getZBDevIoTSetObject() throws IOException {
506 // Translating into the actual Message class
507 MessageGetSimpleDeviceObject sMessage = (MessageGetSimpleDeviceObject) sIoTMasterMsg;
509 // Get IoTSet objects for IP address set on device driver/controller
510 IoTZigbeeAddress objZBDevAddress = new IoTZigbeeAddress(sMessage.getHostAddress());
511 RuntimeOutput.print("IoTSlave: Device address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
512 isetObject.add(objZBDevAddress);
513 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
515 // Send back the received message as acknowledgement
516 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
517 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
523 * A private method to get IoTAddress objects for IoTSet
527 private void getAddIoTSetObject() throws IOException {
529 // Translating into the actual Message class
530 MessageGetSimpleDeviceObject sMessage = (MessageGetSimpleDeviceObject) sIoTMasterMsg;
532 // Get IoTSet objects for IP address set on device driver/controller
533 IoTAddress objAddress = new IoTAddress(sMessage.getHostAddress());
534 RuntimeOutput.print("IoTSlave: Address transferred: " + sMessage.getHostAddress(), BOOL_VERBOSE);
535 isetObject.add(objAddress);
536 RuntimeOutput.print("IoTSlave: This IoTSet now has: " + isetObject.size() + " entry(s)", BOOL_VERBOSE);
537 // Send back the received message as acknowledgement
538 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
539 RuntimeOutput.print("IoTSlave: Getting an object for IoTSet!", BOOL_VERBOSE);
544 * A private method to invoke init() method in the controller object
548 private void invokeInitMethod() throws IOException {
553 Class<?> noparams[] = {};
554 Method method = clsMain.getDeclaredMethod("init", noparams);
555 method.invoke(objMainCls);
556 } catch (NoSuchMethodException |
557 IllegalAccessException |
558 InvocationTargetException ex) {
559 System.out.println("IoTSlave: Exception: "
561 ex.printStackTrace();
566 // Start a new thread to invoke the init function
567 RuntimeOutput.print("IoTSlave: Invoke init method! Job done!", BOOL_VERBOSE);
569 // Send back the received message as acknowledgement
570 outStream.writeObject(new MessageSimple(IoTCommCode.ACKNOWLEDGED));
575 * A public method to do communication with IoTMaster
577 * @params iIndex Integer index
580 public void commIoTMaster() {
584 // Loop, receive and process commands from IoTMaster
585 socket = new Socket(sIoTMasterHostAdd, iComPort);
586 outStream = new ObjectOutputStream(socket.getOutputStream());
587 inStream = new ObjectInputStream(socket.getInputStream());
591 // Get the first payload
592 RuntimeOutput.print("IoTSlave: Slave waiting...", BOOL_VERBOSE);
593 sIoTMasterMsg = (Message) inStream.readObject();
595 // Check payload message from IoTMaster and make a decision
596 switch (sIoTMasterMsg.getMessage()) {
606 case CREATE_MAIN_OBJECT:
610 case CREATE_NEW_IOTSET:
614 case CREATE_NEW_IOTRELATION:
615 createNewIoTRelation();
618 case GET_IOTSET_OBJECT:
622 case GET_IOTRELATION_FIRST_OBJECT:
623 getIoTRelationFirstObject();
626 case GET_IOTRELATION_SECOND_OBJECT:
627 getIoTRelationSecondObject();
630 case REINITIALIZE_IOTSET_FIELD:
631 reinitializeIoTSetField();
634 case REINITIALIZE_IOTRELATION_FIELD:
635 reinitializeIoTRelationField();
638 case GET_DEVICE_IOTSET_OBJECT:
639 getDeviceIoTSetObject();
642 case GET_ZB_DEV_IOTSET_OBJECT:
643 getZBDevIoTSetObject();
646 case GET_ADD_IOTSET_OBJECT:
647 getAddIoTSetObject();
650 case INVOKE_INIT_METHOD:
662 RuntimeOutput.print("IoTSlave: Session ends!", BOOL_VERBOSE);
664 // Closing streams and end session
668 RuntimeOutput.print("IoTSlave: Closing!", BOOL_VERBOSE);
670 } catch (IOException |
671 ClassNotFoundException |
672 NoSuchMethodException |
673 InstantiationException |
674 AlreadyBoundException |
675 IllegalAccessException |
676 InvocationTargetException |
678 NoSuchFieldException ex) {
679 System.out.println("IoTSlave: Exception: "
681 ex.printStackTrace();
685 public static void main(String args[]) {
686 IoTSlave iotSlave = new IoTSlave(args);
687 iotSlave.parseIoTSlaveConfigFile();
688 iotSlave.commIoTMaster();