+G++ := g++
JAVA := java
JAR := jar
JAVADOC := javadoc
# RMI compilation and run
PHONY += rmi
rmi:
+ mkdir -p $(BIN_DIR)
$(JAVAC) -cp . -d $(BIN_DIR) iotrmi/*.java
$(JAVAC) -cp .:../$(BIN_DIR) -d $(BIN_DIR) iotrmi/Java/*.java
$(JAVAC) -cp .:../$(BIN_DIR) -d $(BIN_DIR) iotrmi/Java/sample/*.java
+ mkdir -p $(BIN_DIR)/iotrmi/C++
+ #$(G++) iotrmi/C++/IoTSocketServer.cpp -o $(BIN_DIR)/iotrmi/C++/IoTSocketServer.out
+ #$(G++) iotrmi/C++/IoTSocketClient.cpp -o $(BIN_DIR)/iotrmi/C++/IoTSocketClient.out
+ $(G++) iotrmi/C++/IoTRMICall.cpp -o $(BIN_DIR)/iotrmi/C++/IoTRMICall.out --std=c++11
+ $(G++) iotrmi/C++/IoTRMIObject.cpp -o $(BIN_DIR)/iotrmi/C++/IoTRMIObject.out --std=c++11
PHONY += run-rmiserver
run-rmiserver:
--- /dev/null
+/** Class IoTRMICall provides methods that the upper
+ * layers can use to transport and invoke methods
+ * when using IoTSocket, IoTSocketClient and IoTSocketServer.
+ * <p>
+ * This class serves in the stub part of the RMI
+ * communication. It bridges and creates RMI requests to be
+ * transferred into the RMI object.
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-10-18
+ */
+#ifndef _IOTRMICALL_HPP__
+#define _IOTRMICALL_HPP__
+
+#include <iostream>
+#include <string>
+#include "IoTRMIUtil.hpp"
+#include "IoTSocketClient.hpp"
+
+using namespace std;
+
+class IoTRMICall {
+ public:
+ IoTRMICall(int _port, const char* _address, int _rev, bool* _bResult);
+ ~IoTRMICall();
+ // Public methods
+ int methodLength(string paramCls[], void* paramObj[], int numParam);
+ char* methodToBytes(string methodSign, string paramCls[], void* paramObj[],
+ char* method, int numParam);
+ void* remoteCall(string methodSign, string retType, string paramCls[],
+ void* paramObj[], int numParam, void* retObj);
+
+ private:
+ IoTRMIUtil *rmiUtil;
+ IoTSocketClient *rmiClient;
+};
+
+
+// Constructor
+IoTRMICall::IoTRMICall(int _port, const char* _address, int _rev, bool* _bResult) {
+
+ rmiUtil = new IoTRMIUtil();
+ if (rmiUtil == NULL) {
+ perror("IoTRMICall: IoTRMIUtil isn't initialized!");
+ }
+ rmiClient = new IoTSocketClient(_port, _address, _rev, _bResult);
+ if (rmiClient == NULL) {
+ perror("IoTRMICall: IoTSocketClient isn't initialized!");
+ }
+}
+
+
+// Destructor
+IoTRMICall::~IoTRMICall() {
+
+ // Clean up
+ if (rmiUtil != NULL) {
+
+ delete rmiUtil;
+ rmiUtil = NULL;
+ }
+ if (rmiClient != NULL) {
+
+ fflush(NULL);
+ rmiClient->close();
+ delete rmiClient;
+ rmiClient = NULL;
+ }
+}
+
+
+// Calls a method remotely by passing in parameters and getting a return object
+void* IoTRMICall::remoteCall(string methodSign, string retType, string paramCls[],
+ void* paramObj[], int numParam, void* retObj) {
+
+ // Send input parameters
+ int len = methodLength(paramCls, paramObj, numParam);
+ char method[len];
+ methodToBytes(methodSign, paramCls, paramObj, method, numParam);
+ // Send bytes
+ fflush(NULL);
+ rmiClient->sendBytes(method, len);
+ fflush(NULL);
+ // Receive return value and return it to caller
+ if (retType.compare("void") == 0)
+ // Just make it NULL if it's a void return
+ retObj = NULL;
+ else {
+ int retLen = 0;
+ char* retObjBytes = NULL;
+ retObjBytes = rmiClient->receiveBytes(retObjBytes, &retLen);
+ retObj = IoTRMIUtil::getParamObject(retObj, retType.c_str(), retObjBytes);
+ }
+
+ return retObj;
+}
+
+
+// Find the bytes length of a method
+int IoTRMICall::methodLength(string paramCls[], void* paramObj[], int numParam) {
+
+ // Get byte arrays and calculate method bytes length
+ // Start from the method Id...
+ int methodLen = IoTRMIUtil::METHOD_ID_LEN;
+ for (int i = 0; i < numParam; i++) {
+ // Find the parameter length
+ int paramLen = rmiUtil->getTypeSize(paramCls[i]);
+ if (paramLen == -1) { // Store the length of the field - indefinite length
+ if (paramCls[i].compare("string") == 0) {
+ // Get the length of the string through void* casting to string*
+ paramLen = (*(string*)paramObj[i]).length();
+ }
+ // Some space for param length, i.e. 32 bits for integer
+ methodLen = methodLen + IoTRMIUtil::PARAM_LEN;
+ }
+ // Calculate methodLen
+ methodLen = methodLen + paramLen;
+ }
+
+ return methodLen;
+}
+
+
+// Convert method and its parameters into bytes
+char* IoTRMICall::methodToBytes(string methodSign, string paramCls[],
+ void* paramObj[], char* method, int numParam) {
+
+ // Get method ID in bytes
+ char methodId[IoTRMIUtil::METHOD_ID_LEN];
+ IoTRMIUtil::getHashCodeBytes(methodSign, methodId);
+ memcpy(method, methodId, IoTRMIUtil::METHOD_ID_LEN);
+ int pos = IoTRMIUtil::METHOD_ID_LEN;
+ // Get byte arrays and calculate method bytes length
+ for (int i = 0; i < numParam; i++) {
+ // Find the parameter length
+ int paramLen = rmiUtil->getTypeSize(paramCls[i]);
+ if (paramLen == -1) { // Store the length of the field - indefinite length
+ if (paramCls[i].compare("string") == 0) {
+ // Get the length of the string through void* casting to string*
+ paramLen = (*(string*)paramObj[i]).length();
+ }
+ // Write the parameter length
+ char prmLenBytes[IoTRMIUtil::METHOD_ID_LEN];
+ IoTRMIUtil::intToByteArray(paramLen, prmLenBytes);
+ memcpy(method + pos, prmLenBytes, IoTRMIUtil::PARAM_LEN);
+ pos = pos + IoTRMIUtil::PARAM_LEN;
+ }
+ // Get array of bytes and put it in the array of array of bytes
+ char objBytes[paramLen];
+ IoTRMIUtil::getObjectBytes(objBytes, paramObj[i], paramCls[i].c_str());
+ memcpy(method + pos, objBytes, paramLen);
+ pos = pos + paramLen;
+ }
+
+ return method;
+}
+
+
+#endif
+
+
--- /dev/null
+/** Class IoTRMIObject provides methods that the upper
+ * layers can use to transport and invoke methods
+ * when using IoTSocket, IoTSocketClient and IoTSocketServer.
+ * <p>
+ * This class serves in the skeleton part of the RMI
+ * communication. It instatiate an RMI object and activate
+ * a server process that handles RMI requests.
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-10-24
+ */
+#ifndef _IOTRMIOBJECT_HPP__
+#define _IOTRMIOBJECT_HPP__
+
+#include <iostream>
+#include <string>
+#include "IoTRMIUtil.hpp"
+#include "IoTSocketServer.hpp"
+
+using namespace std;
+
+class IoTRMIObject {
+ public:
+ IoTRMIObject(int _port, bool* _bResult, string _methodSign[], int _size);
+ ~IoTRMIObject();
+ // Public methods
+ void sendReturnObj(void* retObj, string type);
+ void getMethodBytes();
+ string getSignature();
+ void** getMethodParams(string paramCls[], int numParam, void* paramObj[]);
+
+ private:
+ map<int,string> mapHash2Sign;
+ IoTRMIUtil *rmiUtil;
+ IoTSocketServer *rmiServer;
+ char* methodBytes;
+ int methodLen;
+
+ // Private methods
+ void getMethodIds(string methodSign[], int size);
+};
+
+
+// Constructor
+IoTRMIObject::IoTRMIObject(int _port, bool* _bResult, string _methodSign[], int _size) {
+
+ rmiUtil = new IoTRMIUtil();
+ if (rmiUtil == NULL) {
+ perror("IoTRMIObject: IoTRMIUtil isn't initialized!");
+ }
+
+ methodBytes = NULL;
+ methodLen = 0;
+ getMethodIds(_methodSign, _size);
+
+ rmiServer = new IoTSocketServer(_port, _bResult);
+ if (rmiServer == NULL) {
+ perror("IoTRMIObject: IoTSocketServer isn't initialized!");
+ }
+ fflush(NULL);
+ rmiServer->connect();
+ fflush(NULL);
+}
+
+
+// Destructor
+IoTRMIObject::~IoTRMIObject() {
+
+ // Clean up
+ if (rmiUtil != NULL) {
+
+ delete rmiUtil;
+ rmiUtil = NULL;
+ }
+ if (rmiServer != NULL) {
+
+ fflush(NULL);
+ rmiServer->close();
+ delete rmiServer;
+ rmiServer = NULL;
+ }
+}
+
+
+// Send return values in bytes to the caller
+void IoTRMIObject::sendReturnObj(void* retObj, string type) {
+
+ // Find the length of return object in bytes
+ int retLen = rmiUtil->getTypeSize(type);
+ if (retLen == -1) {
+ if (type.compare("string") == 0) {
+ // Get the length of the string through void* casting to string*
+ retLen = (*(string*)retObj).length();
+ }
+ }
+ // Need object bytes variable
+ char retObjBytes[retLen];
+ IoTRMIUtil::getObjectBytes(retObjBytes, retObj, type.c_str());
+ rmiServer->sendBytes(retObjBytes, retLen);
+}
+
+
+// Get method bytes from the socket
+void IoTRMIObject::getMethodBytes() {
+
+ // Get method in bytes and update method length
+ methodBytes = rmiServer->receiveBytes(methodBytes, &methodLen);
+ fflush(NULL);
+ IoTRMIUtil::printBytes(methodBytes, methodLen, false);
+}
+
+
+// Get signature from the method-Id-to-method-signature map
+string IoTRMIObject::getSignature() {
+
+ // Get method Id
+ char methodIdBytes[IoTRMIUtil::METHOD_ID_LEN];
+ memcpy(methodIdBytes, methodBytes, IoTRMIUtil::METHOD_ID_LEN);
+ // Get method object
+ int methodId = 0;
+ IoTRMIUtil::byteArrayToInt(&methodId, methodIdBytes);
+ cout << "Method Id: " << methodId << endl;
+
+ return mapHash2Sign.find(methodId)->second;
+}
+
+
+// Get method parameters and return an array of parameter objects
+//
+// For primitive objects:
+// | 32-bit method ID | m-bit actual data (fixed length) |
+//
+// For string, arrays, and non-primitive objects:
+// | 32-bit method ID | 32-bit length | n-bit actual data | ...
+void** IoTRMIObject::getMethodParams(string paramCls[], int numParam, void* paramObj[]) {
+
+ // Byte scanning position
+ int pos = IoTRMIUtil::METHOD_ID_LEN;
+ for (int i = 0; i < numParam; i++) {
+
+ int paramLen = rmiUtil->getTypeSize(paramCls[i]);
+ // Get the 32-bit field in the byte array to get the actual
+ // length (this is a param with indefinite length)
+ if (paramLen == -1) {
+ char bytPrmLen[IoTRMIUtil::PARAM_LEN];
+ memcpy(bytPrmLen, methodBytes + pos, IoTRMIUtil::PARAM_LEN);
+ pos = pos + IoTRMIUtil::PARAM_LEN;
+ int* prmLenPtr = IoTRMIUtil::byteArrayToInt(¶mLen, bytPrmLen);
+ paramLen = *prmLenPtr;
+ }
+ char paramBytes[paramLen];
+ memcpy(paramBytes, methodBytes + pos, paramLen);
+ pos = pos + paramLen;
+ paramObj[i] = IoTRMIUtil::getParamObject(paramObj[i], paramCls[i].c_str(), paramBytes);
+
+ }
+ // Delete methodBytes
+ delete[] methodBytes;
+
+ return paramObj;
+}
+
+
+// *************
+// Helpers
+// *************
+void IoTRMIObject::getMethodIds(string methodSign[], int size) {
+
+ for(int i = 0; i < size; i++) {
+ int methodId = IoTRMIUtil::hashCode(methodSign[i]);
+ mapHash2Sign[methodId] = methodSign[i];
+ }
+}
+
+
+#endif
+
+
--- /dev/null
+/** Class IoTRMITypes is a class that provides type translations.
+ * <p>
+ * It stores C++ and Java types.
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-10-18
+ */
+#ifndef _IOTRMITYPES_HPP__
+#define _IOTRMITYPES_HPP__
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <vector>
+
+using namespace std;
+
+class IoTRMITypes {
+
+ public:
+ /* Public constants */
+ const static int NUM_PRIMITIVES = 19;
+ const static int NUM_NONPRIMITIVES = 6;
+
+ /**
+ * Primitive data types in Java
+ */
+ const static string primitivesJava[NUM_PRIMITIVES];
+
+
+ /**
+ * Primitive data types in C++ to map the primitives list
+ */
+ const static string primitivesCplus[NUM_PRIMITIVES];
+
+
+ /**
+ * Primitive sizes in Java - Long is 8 bytes and char is 2 bytes
+ */
+ const static int primitivesJavaSizes[NUM_PRIMITIVES];
+
+
+ /**
+ * Primitive sizes in Cplus - Long is 4 bytes and char is 1 byte
+ */
+ const static int primitivesCplusSizes[NUM_PRIMITIVES];
+
+
+ /**
+ * Non-primitive Java data types
+ */
+ const static string nonPrimitivesJava[NUM_NONPRIMITIVES];
+
+
+ /**
+ * Non-primitive C++ data types
+ */
+ const static string nonPrimitivesCplus[NUM_NONPRIMITIVES];
+
+
+ /* Methods */
+ static void arraysToMap(map<string,string> &srcMap, const vector<string> arrKey,
+ const vector<string> arrVal);
+ static void arraysToMap(map<string,int> &srcMap, const vector<string> arrKey,
+ const vector<int> arrVal);
+ static void arraysToMap(map<void*,void*> &srcMap, const vector<void*> arrKey,
+ const vector<void*> arrVal);
+};
+
+
+const string IoTRMITypes::primitivesJava[IoTRMITypes::NUM_PRIMITIVES] = {
+
+ "byte", // 1 byte
+ "Byte", // 1 byte
+ "short", // 2 bytes
+ "Short", // 2 bytes
+ "int", // 4 bytes
+ "Integer", // 4 bytes
+ "long", // 8 bytes
+ "Long", // 8 bytes
+ "float", // 4 bytes
+ "Float", // 4 bytes
+ "double", // 8 bytes
+ "Double", // 8 bytes
+ "boolean", // 1 bytes
+ "Boolean", // 1 bytes
+ "char", // 2 bytes
+ "Character", // 2 bytes
+ "string", // indefinite
+ "String", // indefinite
+ "void" // 0 byte
+};
+
+
+const string IoTRMITypes::primitivesCplus[IoTRMITypes::NUM_PRIMITIVES] = {
+
+ "char", // 1 byte
+ "char", // 1 byte
+ "short", // 2 bytes
+ "short", // 2 bytes
+ "int", // 4 bytes
+ "int", // 4 bytes
+ "int64_t", // 8 bytes
+ "int64_t", // 8 bytes
+ "float", // 4 bytes
+ "float", // 4 bytes
+ "double", // 8 bytes
+ "double", // 8 bytes
+ "bool", // 1 byte
+ "bool", // 1 byte
+ "char", // 2 byte
+ "char", // 2 byte
+ "string", // indefinite
+ "string", // indefinite
+ "void" // 0 byte
+};
+
+
+const int IoTRMITypes::primitivesJavaSizes[IoTRMITypes::NUM_PRIMITIVES] = {
+
+ 1, 1, 2, 2, 4, 4, 8, 8, 4, 4, 8, 8, 1, 1, 2, 2, -1, -1, 0
+};
+
+
+const int IoTRMITypes::primitivesCplusSizes[IoTRMITypes::NUM_PRIMITIVES] = {
+
+ 1, 1, 2, 2, 4, 4, 8, 8, 4, 4, 8, 8, 1, 1, 2, 2, -1, -1, 0
+};
+
+
+const string IoTRMITypes::nonPrimitivesJava[IoTRMITypes::NUM_NONPRIMITIVES] = {
+
+ "Set",
+ "HashSet",
+ "Map",
+ "HashMap",
+ "List",
+ "ArrayList"
+};
+
+
+const string IoTRMITypes::nonPrimitivesCplus[IoTRMITypes::NUM_NONPRIMITIVES] = {
+
+ "set",
+ "unordered_set",
+ "map",
+ "unordered_map",
+ "list",
+ "list"
+};
+
+
+/**================
+ * Helper functions
+ **================
+ */
+// Inserting array members into a Map object
+// that maps arrKey to arrVal objects
+void IoTRMITypes::arraysToMap(map<string,string> &srcMap, const vector<string> arrKey,
+ const vector<string> arrVal) {
+
+ for(int i = 0; i < arrKey.size(); i++) {
+
+ srcMap[arrKey[i]] = arrVal[i];
+ }
+}
+
+
+// Inserting array members into a Map object
+// that maps arrKey to arrVal objects
+void IoTRMITypes::arraysToMap(map<string,int> &srcMap, const vector<string> arrKey,
+ const vector<int> arrVal) {
+
+ for(int i = 0; i < arrKey.size(); i++) {
+
+ srcMap[arrKey[i]] = arrVal[i];
+ }
+}
+
+
+// Inserting array members into a Map object
+// that maps arrKey to arrVal objects
+void IoTRMITypes::arraysToMap(map<void*,void*> &srcMap, const vector<void*> arrKey,
+ const vector<void*> arrVal) {
+
+ for(int i = 0; i < arrKey.size(); i++) {
+
+ srcMap[arrKey[i]] = arrVal[i];
+ }
+}
+
+#endif
--- /dev/null
+/** Class IoTRMIUtil provides methods that the upper
+ * layers can use to transport and invoke methods
+ * when using IoTSocket, IoTSocketClient and IoTSocketServer.
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-10-18
+ */
+#ifndef _IOTRMIUTIL_HPP__
+#define _IOTRMIUTIL_HPP__
+
+#include <stdio.h>
+#include <stdint.h>
+#include <endian.h>
+#include <cxxabi.h>
+#include <cstdlib>
+#include <memory>
+#include <typeinfo>
+
+#include <iostream>
+#include <string>
+#include <string.h>
+
+#include "IoTRMITypes.hpp"
+
+using namespace std;
+
+class IoTRMIUtil {
+
+ public:
+ IoTRMIUtil();
+ //~IoTRMIUtil();
+
+ // Helper functions
+ static void printBytes(char* bytes, const int len, const bool hex);
+ static int hashCode(string str);
+ static char* getHashCodeBytes(string methodSign, char* bytes);
+ int getTypeSize(string type);
+
+ // Primitives to byte array
+ static char* shortToByteArray(short i, char* bytes);
+ static char* intToByteArray(int i, char* bytes);
+ static char* longToByteArray(int64_t i, char* bytes);
+ static char* floatToByteArray(float f, char* bytes);
+ static char* doubleToByteArray(double d, char* bytes);
+ static char* charToByteArray(char c, char* bytes);
+ static char* booleanToByteArray(bool c, char* bytes);
+ static char* stringToByteArray(string c, char* bytes);
+
+ // Byte array to primitives
+ static short* byteArrayToShort(short* result, char* bytes);
+ static int* byteArrayToInt(int* result, char* bytes);
+ static int64_t* byteArrayToLong(int64_t* result, char* bytes);
+ static float* byteArrayToFloat(float* result, char* bytes);
+ static double* byteArrayToDouble(double* result, char* bytes);
+ static char* byteArrayToChar(char* result, char* bytes);
+ static bool* byteArrayToBoolean(bool* result, char* bytes);
+ static string* byteArrayToString(string* result, char* bytes);
+
+ // Get parameter object from byte array
+ static void* getParamObject(void* retObj, const char* type, char* paramBytes);
+ static char* getObjectBytes(char* retObjBytes, void* obj, const char* type);
+
+ // Constants
+ const static int METHOD_ID_LEN = 4; // 4 bytes = 32 bits
+ const static int PARAM_LEN = 4; // 4 bytes = 32 bits (4-byte field that stores the length of the param)
+
+ private:
+ map<string,string> mapPrimitives;
+ map<string,int> mapPrimitiveSizesJava;
+ map<string,int> mapPrimitiveSizesCplus;
+ map<string,string> mapNonPrimitives;
+};
+
+
+// Constructor
+IoTRMIUtil::IoTRMIUtil() {
+
+ // Prepare vectors for inputs
+ std::vector<string> primJava (IoTRMITypes::primitivesJava,
+ IoTRMITypes::primitivesJava + sizeof(IoTRMITypes::primitivesJava)/sizeof(string));
+ std::vector<string> primCplus (IoTRMITypes::primitivesCplus,
+ IoTRMITypes::primitivesCplus + sizeof(IoTRMITypes::primitivesCplus)/sizeof(string));
+ std::vector<int> primJavaSizes (IoTRMITypes::primitivesJavaSizes,
+ IoTRMITypes::primitivesJavaSizes + sizeof(IoTRMITypes::primitivesJavaSizes)/sizeof(int));
+ std::vector<int> primCplusSizes (IoTRMITypes::primitivesCplusSizes,
+ IoTRMITypes::primitivesCplusSizes + sizeof(IoTRMITypes::primitivesCplusSizes)/sizeof(int));
+ std::vector<string> nonPrimJava (IoTRMITypes::nonPrimitivesJava,
+ IoTRMITypes::nonPrimitivesJava + sizeof(IoTRMITypes::nonPrimitivesJava)/sizeof(string));
+ std::vector<string> nonPrimCplus (IoTRMITypes::nonPrimitivesCplus,
+ IoTRMITypes::nonPrimitivesCplus + sizeof(IoTRMITypes::nonPrimitivesCplus)/sizeof(string));
+
+
+ // Write into maps
+ IoTRMITypes::arraysToMap(mapPrimitives, primJava, primCplus);
+ IoTRMITypes::arraysToMap(mapPrimitiveSizesJava, primJava, primJavaSizes);
+ IoTRMITypes::arraysToMap(mapPrimitiveSizesCplus, primJava, primCplusSizes);
+ IoTRMITypes::arraysToMap(mapNonPrimitives, nonPrimJava, nonPrimCplus);
+}
+
+// *************
+// Helpers
+// *************
+void IoTRMIUtil::printBytes(char* bytes, const int len, const bool hex) {
+
+ printf("[ ");
+ for (int i = 0; i < len; i++) {
+ if (hex) // print in hexadecimal
+ printf("%x", bytes[i]);
+ else
+ printf("%d", bytes[i]);
+ if (i < len - 1)
+ printf(", ");
+ }
+ printf(" ]\n");
+}
+
+
+// Return hashCode value
+// This mimics the method Object.hashCode() in Java
+int IoTRMIUtil::hashCode(string str)
+{
+ int hash = 0;
+ int len = str.length();
+ char c;
+ if (len == 0)
+ return hash;
+
+ for (int i = 0; i < len; i++) {
+ c = str.at(i);
+ hash = (31*hash) + (int) c;
+ }
+
+ return hash;
+}
+
+
+char* IoTRMIUtil::getHashCodeBytes(string methodSign, char* bytes) {
+
+ int hash = hashCode(methodSign);
+ return intToByteArray(hash, bytes);
+}
+
+
+int IoTRMIUtil::getTypeSize(string type) {
+
+ // Handle the types and find the sizes
+ if (mapPrimitiveSizesCplus.find(type) != mapPrimitiveSizesCplus.end())
+ return mapPrimitiveSizesCplus.find(type)->second;
+ else
+ return -1; // Size is unknown
+}
+
+
+// Getting parameter object based on received byte array
+void* IoTRMIUtil::getParamObject(void* retObj, const char* type, char* paramBytes) {
+
+ if (strcmp(type, "b") == 0 ||
+ strcmp(type, "byte") == 0) {
+ retObj = (void*) ¶mBytes[0];
+ } else if ( strcmp(type, "s") == 0 ||
+ strcmp(type, "short") == 0) {
+ retObj = (void*) byteArrayToShort((short*) retObj, paramBytes);
+ } else if ( strcmp(type, "i") == 0 ||
+ strcmp(type, "int") == 0) {
+ retObj = (void*) byteArrayToInt((int*) retObj, paramBytes);
+ } else if ( strcmp(type, "l") == 0 ||
+ strcmp(type, "long") == 0) {
+ retObj = (void*) byteArrayToLong((int64_t*) retObj, paramBytes);
+ } else if ( strcmp(type, "f") == 0 ||
+ strcmp(type, "float") == 0) {
+ retObj = (void*) byteArrayToFloat((float*) retObj, paramBytes);
+ } else if ( strcmp(type, "d") == 0 ||
+ strcmp(type, "double") == 0) {
+ retObj = (void*) byteArrayToDouble((double*) retObj, paramBytes);
+ } else if ( strcmp(type, "b") == 0 ||
+ strcmp(type, "bool") == 0) {
+ retObj = (void*) byteArrayToBoolean((bool*) retObj, paramBytes);
+ } else if ( strcmp(type, "c") == 0 ||
+ strcmp(type, "char") == 0) {
+ retObj = (void*) byteArrayToChar((char*) retObj, paramBytes);
+ } else if ( strcmp(type, "Ss") == 0 ||
+ strcmp(type, "string") == 0) {
+ retObj = (void*) byteArrayToString((string*) retObj, paramBytes);
+ } else {
+ string error = "IoTRMIUtil: Unrecognizable type: " + string(type);
+ throw error;
+ }
+
+ return retObj;
+}
+
+
+// Getting byte array based on parameter and its type
+char* IoTRMIUtil::getObjectBytes(char* retObjBytes, void* obj, const char* type) {
+
+ if (strcmp(type, "b") == 0 ||
+ strcmp(type, "byte") == 0) {
+ retObjBytes = (char*) obj;
+ } else if ( strcmp(type, "s") == 0 ||
+ strcmp(type, "short") == 0) {
+ retObjBytes = shortToByteArray(*((short*) obj), retObjBytes);
+ } else if ( strcmp(type, "i") == 0 ||
+ strcmp(type, "int") == 0) {
+ retObjBytes = intToByteArray(*((int*) obj), retObjBytes);
+ } else if ( strcmp(type, "l") == 0 ||
+ strcmp(type, "long") == 0) {
+ retObjBytes = longToByteArray(*((int64_t*) obj), retObjBytes);
+ } else if ( strcmp(type, "f") == 0 ||
+ strcmp(type, "float") == 0) {
+ retObjBytes = floatToByteArray(*((float*) obj), retObjBytes);
+ } else if ( strcmp(type, "d") == 0 ||
+ strcmp(type, "double") == 0) {
+ retObjBytes = doubleToByteArray(*((double*) obj), retObjBytes);
+ } else if ( strcmp(type, "b") == 0 ||
+ strcmp(type, "bool") == 0) {
+ retObjBytes = booleanToByteArray(*((bool*) obj), retObjBytes);
+ } else if ( strcmp(type, "c") == 0 ||
+ strcmp(type, "char") == 0) {
+ retObjBytes = charToByteArray(*((char*) obj), retObjBytes);
+ } else if ( strcmp(type, "Ss") == 0 ||
+ strcmp(type, "string") == 0) {
+ retObjBytes = stringToByteArray(*((string*) obj), retObjBytes);
+ } else {
+ string error = "IoTRMIUtil: Unrecognizable type: " + string(type);
+ throw error;
+ }
+
+ return retObjBytes;
+}
+
+
+// Conversions
+// Primitives to byte array
+char* IoTRMIUtil::shortToByteArray(short s, char* bytes) {
+
+ short sInvert = htobe16(s);
+ //short sInvert = htons(s);
+ memcpy(bytes, &sInvert, sizeof(short));
+
+ return bytes;
+}
+
+
+char* IoTRMIUtil::intToByteArray(int i, char* bytes) {
+
+ int iInvert = htobe32(i);
+ //int iInvert = htonl(i);
+ memcpy(bytes, &iInvert, sizeof(int));
+
+ return bytes;
+}
+
+
+char* IoTRMIUtil::longToByteArray(int64_t l, char* bytes) {
+
+ int64_t lInvert = htobe64(l);
+ memcpy(bytes, &lInvert, sizeof(int64_t));
+
+ return bytes;
+}
+
+
+char* IoTRMIUtil::floatToByteArray(float f, char* bytes) {
+
+ // Copy to int to allow the usage of htobeXX() functions
+ int i = 0;
+ memcpy(&i, &f, sizeof(float));
+ int iInvert = htobe32(i);
+ memcpy(bytes, &iInvert, sizeof(int));
+
+ return bytes;
+}
+
+
+char* IoTRMIUtil::doubleToByteArray(double d, char* bytes) {
+
+ // Copy to int to allow the usage of htobeXX() functions
+ int64_t i = 0;
+ memcpy(&i, &d, sizeof(double));
+ int64_t iInvert = htobe64(i);
+ memcpy(bytes, &iInvert, sizeof(int64_t));
+
+ return bytes;
+}
+
+
+char* IoTRMIUtil::charToByteArray(char c, char* bytes) {
+
+ // We need 2 bytes to accommodate Java char type, whose size is 2
+ bytes[0] = 0;
+ bytes[1] = c;
+
+ return bytes;
+}
+
+
+char* IoTRMIUtil::booleanToByteArray(bool b, char* bytes) {
+
+ bytes[0] = (b) ? 1 : 0;
+ return bytes;
+}
+
+
+char* IoTRMIUtil::stringToByteArray(string str, char* bytes) {
+
+ strcpy(bytes, str.c_str());
+ return bytes;
+}
+
+
+// Conversions
+// Byte array to primitives
+short* IoTRMIUtil::byteArrayToShort(short* result, char* bytes) {
+
+ short s = 0;
+ memcpy(&s, bytes, sizeof(short));
+ //short result = be16toh(s);
+ *result = be16toh(s);
+
+ return result;
+}
+
+
+int* IoTRMIUtil::byteArrayToInt(int* result, char* bytes) {
+
+ int i = 0;
+ memcpy(&i, bytes, sizeof(int));
+ *result = be32toh(i);
+
+ return result;
+}
+
+
+int64_t* IoTRMIUtil::byteArrayToLong(int64_t* result, char* bytes) {
+
+ int64_t l = 0;
+ memcpy(&l, bytes, sizeof(int64_t));
+ *result = be64toh(l);
+
+ return result;
+}
+
+
+float* IoTRMIUtil::byteArrayToFloat(float* result, char* bytes) {
+
+ // Copy to int to allow the usage of beXXtoh() functions
+ int i = 0;
+ memcpy(&i, bytes, sizeof(int));
+ int iInvert = be32toh(i);
+ memcpy(result, &iInvert, sizeof(float));
+
+ return result;
+}
+
+
+double* IoTRMIUtil::byteArrayToDouble(double* result, char* bytes) {
+
+ // Copy to int to allow the usage of beXXtoh() functions
+ int64_t i = 0;
+ memcpy(&i, bytes, sizeof(int64_t));
+ int64_t iInvert = be64toh(i);
+ memcpy(result, &iInvert, sizeof(double));
+
+ return result;
+}
+
+
+char* IoTRMIUtil::byteArrayToChar(char* result, char* bytes) {
+
+ *result = bytes[1];
+ return result;
+}
+
+
+bool* IoTRMIUtil::byteArrayToBoolean(bool* result, char* bytes) {
+
+ *result = (bytes[0]) ? true : false;
+ return result;
+}
+
+
+string* IoTRMIUtil::byteArrayToString(string* result, char* bytes) {
+
+ *result= string(bytes);
+ return result;
+}
+
+#endif
--- /dev/null
+/** Class IoTSocket is a base class for IoTSocketServer.cpp
+ * and IoTSocketClient.cpp that provide interfaces to connect
+ * to either Java or C++ socket endpoint
+ * <p>
+ * Adapted from Java/C++ socket implementation
+ * by Keith Vertanen
+ * @see <a href="https://www.keithv.com/software/socket/</a>
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-10-17
+ */
+#ifndef _IOTSOCKET_HPP__
+#define _IOTSOCKET_HPP__
+
+// Adds in the send/recv acks after each message.
+#define DEBUG_ACK
+
+static const int SOCKET_BUFF_SIZE = 64000;
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+// Duplicated from winsock2.h
+#define SD_RECEIVE 0x00
+#define SD_SEND 0x01
+#define SD_BOTH 0x02
+
+
+class IoTSocket {
+ public:
+ IoTSocket(int iPort, bool* pResult);
+ ~IoTSocket();
+
+ bool close(); // Close the socket
+ bool sendBytes(char* pVals, int _iLen); // Send a set of bytes
+ char* receiveBytes(char* pVals, int* len); // Receive a set of bytes
+
+ protected:
+ int m_iPort; // Port I'm listening on
+ int m_iSock; // Socket connection
+ struct sockaddr_in m_addrRemote; // Connector's address information
+ double* m_pBuffer; // Reuse the same memory for buffer
+
+ private:
+ bool receiveAck();
+ bool sendAck();
+};
+
+
+// Constructor
+IoTSocket::IoTSocket(int iPort, bool* pResult) {
+
+ m_iPort = iPort;
+ m_iSock = -1;
+ m_pBuffer = NULL;
+
+ // Allocate our temporary buffers that are used to convert data types
+ m_pBuffer = (double *) malloc(sizeof(double) * SOCKET_BUFF_SIZE);
+ if (!m_pBuffer) {
+ perror("IoTSocket: Failed to malloc buffer!");
+ return;
+ }
+
+}
+
+
+// Destructor
+IoTSocket::~IoTSocket() {
+
+ if (m_pBuffer) {
+ free(m_pBuffer);
+ m_pBuffer = NULL;
+ }
+}
+
+
+// Send bytes over the wire
+bool IoTSocket::sendBytes(char* pVals, int _iLen) {
+
+ int i = 0;
+ int size[1];
+ int iLen = _iLen;
+ size[0] = iLen;
+
+ if (send(m_iSock, size, 1, 0) == -1) {
+ perror("IoTSocket: Send size error!");
+ return false;
+ }
+
+ if (send(m_iSock, (char *) pVals, iLen, 0) == -1) {
+ perror("IoTSocket: Send bytes error!");
+ return false;
+ }
+#ifdef DEBUG_ACK
+ if (!receiveAck())
+ return false;
+ if (!sendAck())
+ return false;
+#endif
+
+ return true;
+}
+
+
+// Receive bytes, returns number of bytes received
+// Generate an array of char on the heap and return it
+char* IoTSocket::receiveBytes(char* pVals, int* len)
+{
+ int i = 0;
+ int j = 0;
+ char* pTemp = NULL;
+ int iTotalBytes = 0;
+ int iNumBytes = 0;
+ bool bEnd = false;
+
+ int iTotal = 0;
+ int iResult = 0;
+ char size[1];
+ while ((iTotal < 1) && (iResult != -1)) {
+ iResult = recv(m_iSock, size, 1, 0);
+ iTotal += iResult;
+ }
+ if (iResult == -1) {
+ perror("IoTSocket: Receive size error!");
+ return NULL;
+ }
+ int iLen = (int) size[0];
+ // To be returned from this method...
+ *len = iLen;
+ pVals = new char[iLen];
+ pTemp = (char *) m_pBuffer;
+ // We receiving the incoming ints one byte at a time.
+ while (!bEnd) {
+ if ((iNumBytes = recv(m_iSock, pTemp, SOCKET_BUFF_SIZE, 0)) == -1) {
+ perror("IoTSocket: Receive error!");
+ return NULL;
+ }
+ for (i = 0; i < iNumBytes; i++) {
+ pVals[j] = pTemp[i];
+ j++;
+ }
+ iTotalBytes += iNumBytes;
+ if (iTotalBytes == iLen)
+ bEnd = true;
+ }
+#ifdef DEBUG_ACK
+ if (!sendAck())
+ return NULL;
+ if (!receiveAck())
+ return NULL;
+#endif
+
+ return pVals;
+}
+
+
+// Shut down the socket
+bool IoTSocket::close()
+{
+ if (shutdown(m_iSock, SD_BOTH) == -1) {
+ perror("IoTSocket: Close error!");
+ return false;
+ }
+
+ return true;
+}
+
+
+// Receive a short ack from the client
+bool IoTSocket::receiveAck()
+{
+ char temp[1];
+ int iTotal = 0;
+ int iResult = 0;
+ while ((iTotal < 1) && (iResult != -1)) {
+
+ iResult = recv(m_iSock, temp, 1, 0);
+ iTotal += iResult;
+ }
+ if (iResult == -1) {
+
+ perror("IoTSocket: ReceiveAck error!");
+ return false;
+ }
+
+ return true;
+}
+
+
+// Send a short ack to the client
+bool IoTSocket::sendAck()
+{
+ char temp[1];
+ temp[0] = 42;
+
+ if (send(m_iSock, temp, 1, 0) == -1)
+ return false;
+ return true;
+}
+
+#endif
--- /dev/null
+/** Class IoTSocketClient is a communication class
+ * that provides interfaces to connect to either
+ * Java or C++ socket endpoint. It inherits the
+ * methods from IoTSocket.
+ * <p>
+ * Adapted from Java/C++ socket implementation
+ * by Keith Vertanen
+ * @see <a href="https://www.keithv.com/software/socket/</a>
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-08-17
+ */
+#ifndef _IOTSOCKETCLIENT_HPP__
+#define _IOTSOCKETCLIENT_HPP__
+
+#include "IoTSocket.hpp"
+
+class IoTSocketClient : public IoTSocket
+{
+ public:
+ IoTSocketClient(int iPort, const char* pStrHost, bool bReverse, bool* pResult);
+};
+
+
+// Constructor
+IoTSocketClient::IoTSocketClient(int iPort, const char* pStrHost, bool bReverse, bool* pResult) :
+ IoTSocket(iPort, pResult) {
+
+ struct hostent* he = NULL;
+
+ if (pResult)
+ *pResult = false;
+
+ if ((he = gethostbyname(pStrHost)) == NULL) {
+
+ perror("IoTSocketClient: Gethostbyname error!");
+ return;
+ }
+
+ if ((m_iSock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+
+ perror("IoTSocketClient: Socket error!");
+ return;
+ }
+
+ m_addrRemote.sin_family = AF_INET;
+ m_addrRemote.sin_port = htons(m_iPort);
+ m_addrRemote.sin_addr = *((struct in_addr *) he->h_addr);
+ memset(&(m_addrRemote.sin_zero), 0, 8);
+
+ if (connect(m_iSock, (struct sockaddr *) &m_addrRemote, sizeof(struct sockaddr)) == -1) {
+
+ perror("IoTSocketClient: Connect m_iSock error!");
+ return;
+ }
+
+ // Send out request for reversed bits or not
+ char temp[1];
+ if (bReverse) {
+
+ temp[0] = 1;
+ if (send(m_iSock, temp, 1, 0) == -1)
+ {
+ perror("IoTSocketClient: Send 1 error!");
+ return;
+ }
+ } else {
+ temp[0] = 0;
+ if (send(m_iSock, temp, 1, 0) == -1)
+ {
+ perror("IoTSocketClient: Send 2 error!");
+ return;
+ }
+ }
+
+ if (pResult)
+ *pResult = true;
+}
+
+
+#endif
--- /dev/null
+/** Class IoTSocketServer is a communication class
+ * that provides interfaces to connect to either
+ * Java or C++ socket endpoint. It inherits the
+ * methods from IoTSocket.
+ * <p>
+ * Adapted from Java/C++ socket implementation
+ * by Keith Vertanen
+ * @see <a href="https://www.keithv.com/software/socket/</a>
+ *
+ * @author Rahmadi Trimananda <rtrimana @ uci.edu>
+ * @version 1.0
+ * @since 2016-08-17
+ */
+#ifndef _IOTSOCKETSERVER_HPP__
+#define _IOTSOCKETSERVER_HPP__
+
+#include "IoTSocket.hpp"
+
+#define BACKLOG 10 // How many pending connections queue will hold
+
+class IoTSocketServer : public IoTSocket
+{
+ public:
+ IoTSocketServer(int iPort, bool* pResult);
+
+ bool connect(); // Accept a new connection
+
+ protected:
+ bool m_bReverse; // Am I reversing byte order or not?
+ int m_iListen; // Descriptor we are listening on
+ struct sockaddr_in m_addrMe; // My address information
+};
+
+
+// Constructor
+IoTSocketServer::IoTSocketServer(int iPort, bool* pResult) :
+ IoTSocket(iPort, pResult) {
+
+ m_iListen = -1;
+
+ if (pResult)
+ *pResult = false;
+
+ if ((m_iListen = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ perror("IoTSocketServer: Socket error!");
+ return;
+ }
+
+ m_addrMe.sin_family = AF_INET; // Host byte order
+ m_addrMe.sin_port = htons(m_iPort); // Short, network byte order
+ m_addrMe.sin_addr.s_addr = INADDR_ANY; // Auto-fill with my IP
+ memset(&(m_addrMe.sin_zero), 0, 8); // Zero the rest of the struct
+
+ if (bind(m_iListen, (struct sockaddr *) &m_addrMe, sizeof(struct sockaddr)) == -1)
+ {
+ // Note, this can fail if the server has just been shutdown and not enough time has elapsed.
+ // See: http://www.developerweb.net/forum/showthread.php?t=2977
+ perror("IoTSocketServer: Bind error!");
+ return;
+ }
+
+ if (listen(m_iListen, BACKLOG) == -1)
+ {
+ perror("IoTSocketServer: Listen error!");
+ return;
+ }
+
+ if (pResult)
+ *pResult = true;
+}
+
+
+// Wait for somebody to connect to us on our port.
+bool IoTSocketServer::connect()
+{
+ socklen_t iSinSize = (socklen_t) sizeof(struct sockaddr_in);
+
+ if ((m_iSock = accept(m_iListen, (struct sockaddr *) &m_addrRemote, &iSinSize)) == -1)
+ {
+ perror("IoTSocketServer: Accept connection error!");
+ return false;
+ }
+ // The client sends us an int to indicate if we should
+ // be reversing byte order on this connection. The client
+ // is sending 0 or 1, so a reversed 0 still looks
+ // like a 0, no worries mate!
+ char temp[1];
+ int iTotal = 0;
+ int iResult = 0;
+ while ((iTotal < 1) && (iResult != -1))
+ {
+ iResult = recv(m_iSock, temp, 1, 0);
+ iTotal += iResult;
+ }
+ if (iResult == -1)
+ {
+ perror("IoTSocketServer: Receive data error!");
+ return false;
+ }
+
+ int iVal = temp[0];
+
+ if (iVal == 0)
+ m_bReverse = false;
+ else
+ m_bReverse = true;
+
+ return true;
+}
+
+#endif
--- /dev/null
+#include <iostream>
+
+using namespace std;
+
+class TestClass {
+ public:
+ TestClass();
+ TestClass(int _int, float _float, string _string);
+ ~TestClass();
+
+ void setA(int _int);
+ void setB(float _float);
+ void setC(string _string);
+ string sumArray(const string newA[]);
+ int setAndGetA(int newA);
+ int setACAndGetA(string newC, int newA);
+ //void registerCallback(CallBackInterface _cb);
+ //int callBack();
+
+ private:
+ int intA;
+ float floatB;
+ string stringC;
+ //CallBackInterface cb;
+
+};
+
+
+TestClass::TestClass() {
+
+ intA = 1;
+ floatB = 2;
+ stringC = "345";
+ //cb = NULL;
+}
+
+
+TestClass::TestClass(int _int, float _float, string _string) {
+
+ intA = _int;
+ floatB = _float;
+ stringC = _string;
+ //cb = NULL;
+}
+
+
+void TestClass::setA(int _int) {
+
+ intA = _int;
+}
+
+
+void TestClass::setB(float _float) {
+
+ floatB = _float;
+}
+
+
+void TestClass::setC(string _string) {
+
+ stringC = _string;
+}
+
+
+string TestClass::sumArray(const string newA[]) {
+
+ string sum = "";
+ int len = sizeof(newA) / sizeof(string);
+ for(int c = 0; c < len; c++) {
+ sum = sum + newA[c];
+ }
+ return sum;
+}
+
+
+int TestClass::setAndGetA(int newA) {
+
+ intA = newA;
+ return intA;
+}
+
+
+int TestClass::setACAndGetA(string newC, int newA) {
+
+ stringC = newC;
+ intA = newA;
+ return intA;
+}
+
+
+/*void TestClass::registerCallback(CallBackInterface _cb) {
+
+ cb = _cb;
+}
+
+
+int TestClass::callBack() {
+
+ return cb.printInt();
+}*/
+
int numbParam = paramObj.length;
int methodLen = IoTRMIUtil.METHOD_ID_LEN; // Initialized to the length of method ID
byte[][] objBytesArr = new byte[numbParam][];
- for (int i=0; i < numbParam; i++) {
+ for (int i = 0; i < numbParam; i++) {
// Get byte arrays for the objects
objBytesArr[i] = IoTRMIUtil.getObjectBytes(paramObj[i]);
String clsName = paramCls[i].getSimpleName();
int paramLen = rmiUtil.getTypeSize(clsName);
- if (paramLen == -1) { // indefinite length
+ if (paramLen == -1) { // indefinite length - store the length first
methodLen = methodLen + IoTRMIUtil.PARAM_LEN;
}
methodLen = methodLen + objBytesArr[i].length;
System.arraycopy(methodId, 0, method, 0, methodId.length);
pos = pos + IoTRMIUtil.METHOD_ID_LEN;
// Second iteration for copying bytes
- for (int i=0; i < numbParam; i++) {
+ for (int i = 0; i < numbParam; i++) {
String clsName = paramCls[i].getSimpleName();
int paramLen = rmiUtil.getTypeSize(clsName);
*/
public String getSignature() {
- // Get method ID
+ // Get method Id bytes
byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
System.arraycopy(methodBytes, 0, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
- // Get Method object to handle method
+ // Get method Id
int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
// Get method signature from the Map
return mapHash2Sign.get(methodId);
}
return obj;
}
+
+
+ public static void main(String[] args) {
+
+ boolean data = false;
+ //char data = 'c';
+ //float data = 12.5123f;
+ //double data = 12.51231234;
+ //long data = 123456781234l;
+ //short data = 1234;
+ //int data = 12345678;
+ byte[] result = booleanToByteArray(data);
+ System.out.println("Result: " + Arrays.toString(result));
+ System.out.println("Converted back: " + byteArrayToBoolean(result));
+
+ String str = "methodA(int,string,float,double,double)";
+ int hash = str.hashCode();
+ System.out.println("Hash value: " + hash);
+ }
}
intA = _int;
floatB = _float;
stringC = _string;
+ cb = null;
}
public int callBack() {
- System.out.println("This callBack function is called inside TestClass!");
return cb.printInt();
}