From e8202b98639eb5d07f3342e920d0bcdc23503c3d Mon Sep 17 00:00:00 2001
From: rtrimana <rtrimana@uci.edu>
Date: Tue, 1 Nov 2016 12:24:35 -0700
Subject: [PATCH] Initial version that handles multiple callback objects
 through 1 socket

---
 iotjava/Makefile                              |  12 +-
 iotjava/iotrmi/Java/IoTRMICall.java           |  17 +-
 iotjava/iotrmi/Java/IoTRMIObject.java         |  71 ++++--
 iotjava/iotrmi/Java/IoTRMIUtil.java           |   1 +
 .../Java/sample/CallBack_CBSkeleton.java      |  76 ++++++
 .../iotrmi/Java/sample/CallBack_CBStub.java   |  70 ++++++
 .../iotrmi/Java/sample/CallBack_Skeleton.java |  66 +++--
 iotjava/iotrmi/Java/sample/CallBack_Stub.java |  14 +-
 iotjava/iotrmi/Java/sample/TestClass.java     |  27 ++-
 .../Java/sample/TestClassInterface.java       |   1 +
 .../Java/sample/TestClass_CBSkeleton.java     | 167 +++++++++++++
 .../iotrmi/Java/sample/TestClass_CBStub.java  | 225 ++++++++++++++++++
 .../Java/sample/TestClass_Skeleton.java       | 183 ++++++++++----
 .../iotrmi/Java/sample/TestClass_Stub.java    | 115 +++++++--
 14 files changed, 919 insertions(+), 126 deletions(-)
 create mode 100644 iotjava/iotrmi/Java/sample/CallBack_CBSkeleton.java
 create mode 100644 iotjava/iotrmi/Java/sample/CallBack_CBStub.java
 create mode 100644 iotjava/iotrmi/Java/sample/TestClass_CBSkeleton.java
 create mode 100644 iotjava/iotrmi/Java/sample/TestClass_CBStub.java

diff --git a/iotjava/Makefile b/iotjava/Makefile
index 9b858f7..d9814d8 100644
--- a/iotjava/Makefile
+++ b/iotjava/Makefile
@@ -36,12 +36,12 @@ rmi:
 	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
-	mkdir -p $(BIN_DIR)/iotrmi/C++/sample
-	$(G++) iotrmi/C++/sample/TestClass.cpp -o $(BIN_DIR)/iotrmi/C++/sample/TestClass.out --std=c++11
-	$(G++) iotrmi/C++/sample/TestClass_Stub.cpp -o $(BIN_DIR)/iotrmi/C++/sample/TestClass_Stub.out --std=c++11
-	$(G++) iotrmi/C++/sample/TestClass_Skeleton.cpp -o $(BIN_DIR)/iotrmi/C++/sample/TestClass_Skeleton.out --std=c++11
+#	$(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
+#	mkdir -p $(BIN_DIR)/iotrmi/C++/sample
+#	$(G++) iotrmi/C++/sample/TestClass.cpp -o $(BIN_DIR)/iotrmi/C++/sample/TestClass.out --std=c++11
+#	$(G++) iotrmi/C++/sample/TestClass_Stub.cpp -o $(BIN_DIR)/iotrmi/C++/sample/TestClass_Stub.out --std=c++11
+#	$(G++) iotrmi/C++/sample/TestClass_Skeleton.cpp -o $(BIN_DIR)/iotrmi/C++/sample/TestClass_Skeleton.out --std=c++11
 
 PHONY += run-rmiserver
 run-rmiserver:
diff --git a/iotjava/iotrmi/Java/IoTRMICall.java b/iotjava/iotrmi/Java/IoTRMICall.java
index 433c659..8bd86c9 100644
--- a/iotjava/iotrmi/Java/IoTRMICall.java
+++ b/iotjava/iotrmi/Java/IoTRMICall.java
@@ -48,11 +48,11 @@ public class IoTRMICall {
 	/**
 	 * remoteCall() calls a method remotely by passing in parameters and getting a return Object
 	 */
-	public Object remoteCall(String methodSign, Class<?> retType, Class<?> retGenTypeKey, 
+	public Object remoteCall(int objectId, String methodSign, Class<?> retType, Class<?> retGenTypeKey, 
 			Class<?> retGenTypeVal, Class<?>[] paramCls, Object[] paramObj) {
 
 		// Send method info
-		byte[] methodBytes = methodToBytes(methodSign, paramCls, paramObj);
+		byte[] methodBytes = methodToBytes(objectId, methodSign, paramCls, paramObj);
 		try {
 			rmiClient.sendBytes(methodBytes);
 		} catch (IOException ex) {
@@ -78,15 +78,17 @@ public class IoTRMICall {
 	/**
 	 * methodToBytes() returns byte representation of a method
 	 */
-	public byte[] methodToBytes(String methodSign, Class<?>[] paramCls, Object[] paramObj) {
+	public byte[] methodToBytes(int objectId, String methodSign, Class<?>[] paramCls, Object[] paramObj) {
 
+		// Initialized to the length of method ID
+		int methodLen = IoTRMIUtil.OBJECT_ID_LEN;
+		byte[] objId = IoTRMIUtil.intToByteArray(objectId);
 		// Get method ID in bytes
 		int methId = listMethodId.indexOf(methodSign);
 		byte[] methodId = IoTRMIUtil.intToByteArray(methId);
-
 		// Get byte arrays and calculate method bytes length
 		int numbParam = paramObj.length;
-		int methodLen = IoTRMIUtil.METHOD_ID_LEN;	// Initialized to the length of method ID
+		methodLen = methodLen + IoTRMIUtil.METHOD_ID_LEN;
 		byte[][] objBytesArr = new byte[numbParam][];
 		for (int i = 0; i < numbParam; i++) {
 			// Get byte arrays for the objects
@@ -98,11 +100,12 @@ public class IoTRMICall {
 			}
 			methodLen = methodLen + objBytesArr[i].length;
 		}
-
 		// Construct method in byte array
 		byte[] method = new byte[methodLen];
 		int pos = 0;
-		System.arraycopy(methodId, 0, method, 0, methodId.length);
+		System.arraycopy(objId, 0, method, 0, IoTRMIUtil.METHOD_ID_LEN);
+		pos = pos + IoTRMIUtil.OBJECT_ID_LEN;
+		System.arraycopy(methodId, 0, method, pos, IoTRMIUtil.METHOD_ID_LEN);
 		pos = pos + IoTRMIUtil.METHOD_ID_LEN;
 		// Second iteration for copying bytes
 		for (int i = 0; i < numbParam; i++) {
diff --git a/iotjava/iotrmi/Java/IoTRMIObject.java b/iotjava/iotrmi/Java/IoTRMIObject.java
index 3ecd6bf..043c28a 100644
--- a/iotjava/iotrmi/Java/IoTRMIObject.java
+++ b/iotjava/iotrmi/Java/IoTRMIObject.java
@@ -50,23 +50,51 @@ public class IoTRMIObject {
 
 
 	/**
-	 * sendReturnObj() sends back return Object to client
+	 * getMethodBytes() waits for method transmission in bytes
 	 */
-	public void sendReturnObj(Object retObj) throws IOException {
+	public byte[] getMethodBytes() throws IOException {
 
-		// Send back return value
-		byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
-		rmiServer.sendBytes(retObjBytes);
+		// Receive method info
+		methodBytes = rmiServer.receiveBytes(methodBytes);
+		return methodBytes;
 	}
 
 
 	/**
-	 * getMethodBytes() waits for method transmission in bytes
+	 * getObjectId() gets object Id from bytes
 	 */
-	public void getMethodBytes() throws IOException {
+	public int getObjectId() {
+
+		// Get object Id bytes
+		byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
+		System.arraycopy(methodBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
+		// Get object Id
+		int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
+		return objectId;
+	}
 
-		// Receive method info
-		methodBytes = rmiServer.receiveBytes(methodBytes);
+
+	/**
+	 * static version of getObjectId()
+	 */
+	public static int getObjectId(byte[] methodBytes) {
+
+		// Get object Id bytes
+		byte[] objectIdBytes = new byte[IoTRMIUtil.OBJECT_ID_LEN];
+		System.arraycopy(methodBytes, 0, objectIdBytes, 0, IoTRMIUtil.OBJECT_ID_LEN);
+		// Get object Id
+		int objectId = IoTRMIUtil.byteArrayToInt(objectIdBytes);
+		return objectId;
+	}
+
+
+	/**
+	 * setMethodBytes() sets bytes for method
+	 */
+	public void setMethodBytes(byte[] _methodBytes) throws IOException {
+
+		// Set method bytes
+		methodBytes = _methodBytes;
 	}
 
 
@@ -77,7 +105,8 @@ public class IoTRMIObject {
 
 		// Get method Id bytes
 		byte[] methodIdBytes = new byte[IoTRMIUtil.METHOD_ID_LEN];
-		System.arraycopy(methodBytes, 0, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
+		// Method Id is positioned after object Id in the byte array
+		System.arraycopy(methodBytes, IoTRMIUtil.OBJECT_ID_LEN, methodIdBytes, 0, IoTRMIUtil.METHOD_ID_LEN);
 		// Get method Id
 		int methodId = IoTRMIUtil.byteArrayToInt(methodIdBytes);
 		// Get method signature from the list
@@ -89,23 +118,24 @@ public class IoTRMIObject {
 	 * getMethodParams() gets method params based on byte array received
 	 * <p>
 	 * Basically this is the format of a method in bytes:
-	 * 1) 32-bit value of method ID (hash code)
-	 * 2) m parameters with n-bit value each (m x n-bit)
+	 * 1) 32-bit value of object ID
+	 * 2) 32-bit value of method ID
+	 * 3) m parameters with n-bit value each (m x n-bit)
 	 * For the parameters that don't have definite length,
 	 * we need to extract the length from a preceding 32-bit
 	 * field in front of it.
 	 *
 	 * For primitive objects:
-	 * | 32-bit method ID | m-bit actual data (fixed length)  |
+	 * | 32-bit object ID | 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 | ...
+	 * | 32-bit object ID | 32-bit method ID | 32-bit length | n-bit actual data | ...
 	 * 
 	 */
 	public Object[] getMethodParams(Class<?>[] arrCls, Class<?>[] arrGenKeyCls, Class<?>[] arrGenValCls) {
 
 		// Byte scanning position
-		int pos = IoTRMIUtil.METHOD_ID_LEN;
+		int pos = IoTRMIUtil.OBJECT_ID_LEN + IoTRMIUtil.METHOD_ID_LEN;
 		Object[] paramObj = new Object[arrCls.length];
 		for (int i=0; i < arrCls.length; i++) {
 
@@ -128,4 +158,15 @@ public class IoTRMIObject {
 
 		return paramObj;
 	}
+
+
+	/**
+	 * sendReturnObj() sends back return Object to client
+	 */
+	public void sendReturnObj(Object retObj) throws IOException {
+
+		// Send back return value
+		byte[] retObjBytes = IoTRMIUtil.getObjectBytes(retObj);
+		rmiServer.sendBytes(retObjBytes);
+	}
 }
diff --git a/iotjava/iotrmi/Java/IoTRMIUtil.java b/iotjava/iotrmi/Java/IoTRMIUtil.java
index a382b0c..e1c1be8 100644
--- a/iotjava/iotrmi/Java/IoTRMIUtil.java
+++ b/iotjava/iotrmi/Java/IoTRMIUtil.java
@@ -37,6 +37,7 @@ public class IoTRMIUtil {
 	/**
 	 * Class Constants
 	 */
+	public final static int OBJECT_ID_LEN = 4; 	// 4 bytes = 32 bits
 	public final static int METHOD_ID_LEN = 4; 	// 4 bytes = 32 bits
 	public final static int PARAM_LEN = 4; 		// 4 bytes = 32 bits (4-byte field that stores the length of the param)
 
diff --git a/iotjava/iotrmi/Java/sample/CallBack_CBSkeleton.java b/iotjava/iotrmi/Java/sample/CallBack_CBSkeleton.java
new file mode 100644
index 0000000..c038ec7
--- /dev/null
+++ b/iotjava/iotrmi/Java/sample/CallBack_CBSkeleton.java
@@ -0,0 +1,76 @@
+package iotrmi.Java.sample;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.Arrays;
+import iotrmi.Java.IoTRMIObject;
+
+public class CallBack_CBSkeleton implements CallBackInterface {
+
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
+
+		"intprintInt()",
+		"voidsetInt(int)"
+	};
+	private CallBackInterface cb;
+
+
+	/**
+	 * Constructors
+	 */
+	public CallBack_CBSkeleton(CallBackInterface _cb, int _objectId) throws
+		ClassNotFoundException, InstantiationException,
+			IllegalAccessException, IOException {
+
+		cb = _cb;
+		objectId = _objectId;
+		System.out.println("Creating CallBack_Skeleton and waiting!");
+	}
+
+
+	public Object invokeMethod(IoTRMIObject rmiObj) throws IOException {
+
+		String methodSign = rmiObj.getSignature();
+		Object[] paramObj = null;
+		Object retObj = null;
+
+		if (methodSign.equals("intprintInt()")) {
+			retObj = printInt();
+		} else if (methodSign.equals("voidsetInt(int)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
+				new Class<?>[] { null }, new Class<?>[] { null });
+			setInt((int) paramObj[0]);
+		} else
+			throw new Error("Signature not recognized!");
+		System.out.println("Return object: " + retObj);
+
+		return retObj;
+	}
+
+
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+
+
+	public int printInt() {
+		return cb.printInt();
+	}
+
+
+	public void setInt(int _i) {
+		cb.setInt(_i);
+	}
+
+
+	public static void main(String[] args) throws Exception {
+
+		int port = 5010;
+		CallBack cb = new CallBack(23);
+		CallBack_Skeleton cbSkel = new CallBack_Skeleton(cb, port);
+		cbSkel.waitRequestInvokeMethod();
+	}
+}
diff --git a/iotjava/iotrmi/Java/sample/CallBack_CBStub.java b/iotjava/iotrmi/Java/sample/CallBack_CBStub.java
new file mode 100644
index 0000000..fea29b1
--- /dev/null
+++ b/iotjava/iotrmi/Java/sample/CallBack_CBStub.java
@@ -0,0 +1,70 @@
+package iotrmi.Java.sample;
+
+import java.io.IOException;
+import iotrmi.Java.IoTRMICall;
+
+public class CallBack_CBStub implements CallBackInterface {
+
+	/**
+	 * Class Properties
+	 */
+	private IoTRMICall rmiCall;
+	private String address;
+
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
+
+		"intprintInt()",
+		"voidsetInt(int)"
+	};
+
+	/**
+	 * Constructors
+	 */
+	public CallBack_CBStub(IoTRMICall _rmiCall, int _objectId, String _address) throws IOException {
+
+		address = _address;
+		objectId = _objectId;
+		rmiCall = _rmiCall;
+	}
+
+
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+
+
+	public int printInt() {
+
+		String sign = "intprintInt()";
+		Class<?> retType = int.class;
+		Class<?>[] paramCls = new Class<?>[] { };
+		Object[] paramObj = new Object[] { };
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+		return (int)retObj;
+	}
+
+
+	public void setInt(int _i) {
+
+		String sign = "voidsetInt(int)";
+		Class<?> retType = void.class;
+		Class<?>[] paramCls = new Class<?>[] { int.class };
+		Object[] paramObj = new Object[] { _i };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	public static void main(String[] args) throws Exception {
+
+		int port = 5010;
+		String address = "localhost";
+		int rev = 0;
+
+		CallBack_Stub cbstub = new CallBack_Stub(port, address, rev);
+		cbstub.setInt(23);
+		cbstub.printInt();
+	}
+}
diff --git a/iotjava/iotrmi/Java/sample/CallBack_Skeleton.java b/iotjava/iotrmi/Java/sample/CallBack_Skeleton.java
index 46e0df6..70d8a81 100644
--- a/iotjava/iotrmi/Java/sample/CallBack_Skeleton.java
+++ b/iotjava/iotrmi/Java/sample/CallBack_Skeleton.java
@@ -5,14 +5,14 @@ import java.util.Set;
 import java.util.Arrays;
 import iotrmi.Java.IoTRMIObject;
 
-public class CallBack_Skeleton {
+public class CallBack_Skeleton implements CallBackInterface {
 
-	private String[] methodSignatures = {
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
 
 		"intprintInt()",
 		"voidsetInt(int)"
 	};
-
 	private CallBackInterface cb;
 	private IoTRMIObject rmiObj;
 
@@ -36,31 +36,51 @@ public class CallBack_Skeleton {
 		while (true) {
 
 			rmiObj.getMethodBytes();
-			String methodSign = rmiObj.getSignature();
-			Object[] paramObj = null;
-			Object retObj = null;
-			System.out.println("Method sign: " + methodSign);
-
-			if (methodSign.equals("intprintInt()")) {
-				//paramObj = rmiObj.getMethodParams(new Class<?>[] { }, 
-				//	new Class<?>[] { null }, new Class<?>[] { null });
-				retObj = cb.printInt();
-			} else if (methodSign.equals("voidsetInt(int)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
-					new Class<?>[] { null }, new Class<?>[] { null });
-				cb.setInt((int) paramObj[0]);
-			} else
-				throw new Error("Signature un-recognized!");
-			System.out.println("Return object: " + retObj);
-
-			if (retObj != null) {
-				rmiObj.sendReturnObj(retObj);
+			int objId = rmiObj.getObjectId();
+			if (objId == objectId) {
+			// Multiplex based on object Id
+				rmiObj.getMethodBytes();
+				String methodSign = rmiObj.getSignature();
+				Object[] paramObj = null;
+				Object retObj = null;
+				System.out.println("Method sign: " + methodSign);
+
+				if (methodSign.equals("intprintInt()")) {
+					retObj = printInt();
+				} else if (methodSign.equals("voidsetInt(int)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
+						new Class<?>[] { null }, new Class<?>[] { null });
+					setInt((int) paramObj[0]);
+				} else
+					throw new Error("Signature not recognized!");
+				System.out.println("Return object: " + retObj);
+
+				if (retObj != null) {
+					rmiObj.sendReturnObj(retObj);
+				}
+				System.out.println("Servicing remote call for method: " + methodSign);
 			}
-			System.out.println("Servicing remote call for method: " + methodSign);
 		}
 	}
 
 
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+
+
+	public int printInt() {
+		return cb.printInt();
+	}
+
+
+	public void setInt(int _i) {
+		cb.setInt(_i);
+	}
+
+
 	public static void main(String[] args) throws Exception {
 
 		int port = 5010;
diff --git a/iotjava/iotrmi/Java/sample/CallBack_Stub.java b/iotjava/iotrmi/Java/sample/CallBack_Stub.java
index 0ea9728..4b9b786 100644
--- a/iotjava/iotrmi/Java/sample/CallBack_Stub.java
+++ b/iotjava/iotrmi/Java/sample/CallBack_Stub.java
@@ -10,7 +10,8 @@ public class CallBack_Stub implements CallBackInterface {
 	 */
 	private IoTRMICall rmiCall;
 
-	private String[] methodSignatures = {
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
 
 		"intprintInt()",
 		"voidsetInt(int)"
@@ -25,13 +26,20 @@ public class CallBack_Stub implements CallBackInterface {
 	}
 
 
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+
+
 	public int printInt() {
 
 		String sign = "intprintInt()";
 		Class<?> retType = int.class;
 		Class<?>[] paramCls = new Class<?>[] { };
 		Object[] paramObj = new Object[] { };
-		Object retObj = rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 		return (int)retObj;
 	}
 
@@ -42,7 +50,7 @@ public class CallBack_Stub implements CallBackInterface {
 		Class<?> retType = void.class;
 		Class<?>[] paramCls = new Class<?>[] { int.class };
 		Object[] paramObj = new Object[] { _i };
-		rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 	}
 
 
diff --git a/iotjava/iotrmi/Java/sample/TestClass.java b/iotjava/iotrmi/Java/sample/TestClass.java
index dc6f73e..07a1679 100644
--- a/iotjava/iotrmi/Java/sample/TestClass.java
+++ b/iotjava/iotrmi/Java/sample/TestClass.java
@@ -1,6 +1,8 @@
 package iotrmi.Java.sample;
 
 import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
 
 public class TestClass implements TestClassInterface {
 
@@ -11,6 +13,7 @@ public class TestClass implements TestClassInterface {
 	private float floatB;
 	private String stringC;
 	private CallBackInterface cb;
+	private List<CallBackInterface> cblist;
 
 	/**
 	 * Constructors
@@ -21,6 +24,7 @@ public class TestClass implements TestClassInterface {
 		floatB = 2;
 		stringC = "345";
 		cb = null;
+		cblist = new ArrayList<CallBackInterface>();
 	}
 
 
@@ -30,6 +34,7 @@ public class TestClass implements TestClassInterface {
 		floatB = _float;
 		stringC = _string;
 		cb = null;
+		cblist = new ArrayList<CallBackInterface>();
 	}
 
 
@@ -82,9 +87,29 @@ public class TestClass implements TestClassInterface {
 	}
 
 
+	public void registerCallback(CallBackInterface[] _cb) {
+
+		for (CallBackInterface cb : _cb) {
+			cblist.add(cb);
+			System.out.println("Registering callback object!");
+		}
+	}
+
+
+	//public int callBack() {
+	//	return cb.printInt();
+	//}
+
+
 	public int callBack() {
 
-		return cb.printInt();
+		int sum = 0;
+		for (CallBackInterface cb : cblist) {
+			sum = sum + cb.printInt();
+		}
+		//sum = cblist.get(1).printInt();
+
+		return sum;
 	}
 
 
diff --git a/iotjava/iotrmi/Java/sample/TestClassInterface.java b/iotjava/iotrmi/Java/sample/TestClassInterface.java
index d41166d..b9c563a 100644
--- a/iotjava/iotrmi/Java/sample/TestClassInterface.java
+++ b/iotjava/iotrmi/Java/sample/TestClassInterface.java
@@ -11,5 +11,6 @@ public interface TestClassInterface {
 	public int setAndGetA(int newA);
 	public int setACAndGetA(String newC, int newA);
 	public void registerCallback(CallBackInterface _cb);
+	public void registerCallback(CallBackInterface[] _cb);
 	public int callBack();
 }
diff --git a/iotjava/iotrmi/Java/sample/TestClass_CBSkeleton.java b/iotjava/iotrmi/Java/sample/TestClass_CBSkeleton.java
new file mode 100644
index 0000000..770696b
--- /dev/null
+++ b/iotjava/iotrmi/Java/sample/TestClass_CBSkeleton.java
@@ -0,0 +1,167 @@
+package iotrmi.Java.sample;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.HashMap;
+
+import iotrmi.Java.IoTRMIObject;
+import iotrmi.Java.IoTRMICall;
+
+public class TestClass_CBSkeleton implements TestClassInterface {
+
+	/**
+	 * Class Constants
+	 */
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
+
+		"voidsetA(int)",
+		"voidsetB(float)",
+		"voidsetC(string)",
+		"sumArray(string[])",
+		"intsetAndGetA(int)",
+		"intsetACAndGetA(string,int)",
+		"intcallBack()",
+		"voidregisterCallBack(CallBackInterface)",
+		"voidregisterCallBack(CallBackInterface[])"
+	};
+
+	private TestClassInterface tc;
+	private int port;
+	private CallBackInterface cbstub;
+
+
+	/**
+	 * Constructors
+	 */
+	public TestClass_CBSkeleton(TestClass _tc, int _objectId) throws
+		ClassNotFoundException, InstantiationException,
+			IllegalAccessException, IOException {
+
+		tc = _tc;
+		objectId = _objectId;
+		System.out.println("Creating object with object Id: " + objectId);
+	}
+
+
+	// Callback object multiplexing
+	public Object invokeMethod(IoTRMIObject rmiObj) throws IOException {
+
+		String methodSign = rmiObj.getSignature();
+		Object[] paramObj = null;
+		Object retObj = null;
+
+		if (methodSign.equals("voidsetA(int)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
+				new Class<?>[] { null }, new Class<?>[] { null });
+			setA((int) paramObj[0]);
+		} else if (methodSign.equals("voidsetB(float)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { float.class }, 
+				new Class<?>[] { null }, new Class<?>[] { null });
+			setB((float) paramObj[0]);
+		} else if (methodSign.equals("voidsetC(string)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { String.class }, 
+				new Class<?>[] { null }, new Class<?>[] { null });
+			setC((String) paramObj[0]);
+		} else if (methodSign.equals("sumArray(string[])")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { String[].class }, 
+				new Class<?>[] { null }, new Class<?>[] { null });
+			retObj = sumArray((String[]) paramObj[0]);
+		} else if (methodSign.equals("intsetAndGetA(int)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
+				new Class<?>[] { null }, new Class<?>[] { null });
+			retObj = setAndGetA((int) paramObj[0]);
+		} else if (methodSign.equals("intsetACAndGetA(string,int)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { String.class, int.class }, 
+				new Class<?>[] { null, null }, new Class<?>[] { null, null });
+			retObj = setACAndGetA((String) paramObj[0], (int) paramObj[1]);
+		} else if (methodSign.equals("voidregisterCallBack(CallBackInterface)")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class, String.class, int.class }, 
+				new Class<?>[] { null, null, null }, new Class<?>[] { null, null, null });
+			CallBackInterface cbstub = new CallBack_Stub((int) paramObj[0], (String) paramObj[1], (int) paramObj[2]);
+			registerCallback((CallBackInterface) cbstub);
+		} else if (methodSign.equals("voidregisterCallBack(CallBackInterface[])")) {
+			paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class, String.class, int.class, int.class }, 
+				new Class<?>[] { null, null, null, null }, new Class<?>[] { null, null, null, null });
+			String[] methodSignatures = TestClass_Stub.getMethodSignatures();
+			IoTRMICall rmiCall = new IoTRMICall((int) paramObj[0], (String) paramObj[1], (int) paramObj[2], methodSignatures);
+			int numStubs = (int) paramObj[3];
+			CallBackInterface[] stub = new CallBackInterface[numStubs];
+			for (int objId = 0; objId < numStubs; objId++) {
+				stub[objId] = new CallBack_CBStub(rmiCall, objId, (String) paramObj[1]);
+			}
+			registerCallback(stub);
+		} else if (methodSign.equals("intcallBack()")) {
+			retObj = callBack();
+		} else
+			throw new Error("Signature not recognized!");
+
+		return retObj;
+	}
+
+
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+	
+	
+	public void setA(int _int) {
+		
+		tc.setA(_int);
+	}
+	
+	
+	public void setB(float _float) {
+		
+		tc.setB(_float);
+	}
+	
+	
+	public void setC(String _string) {
+		
+		tc.setC(_string);
+	}
+	
+	
+	public String sumArray(String[] newA) {
+		
+		return tc.sumArray(newA);
+	}
+	
+	
+	public int setAndGetA(int newA) {
+		
+		return tc.setAndGetA(newA);
+	}
+	
+	
+	public int setACAndGetA(String newC, int newA) {
+		
+		return tc.setACAndGetA(newC, newA);
+	}
+	
+	
+	public void registerCallback(CallBackInterface _cb) {
+		
+		tc.registerCallback(_cb);
+	}
+
+	public void registerCallback(CallBackInterface[] _cb) {
+		
+		tc.registerCallback(_cb);
+	}
+	
+	public int callBack() {
+		
+		return tc.callBack();
+	}
+
+
+	public static void main(String[] args) throws Exception {
+
+	}
+}
diff --git a/iotjava/iotrmi/Java/sample/TestClass_CBStub.java b/iotjava/iotrmi/Java/sample/TestClass_CBStub.java
new file mode 100644
index 0000000..798e398
--- /dev/null
+++ b/iotjava/iotrmi/Java/sample/TestClass_CBStub.java
@@ -0,0 +1,225 @@
+package iotrmi.Java.sample;
+
+import java.io.IOException;
+import iotrmi.Java.IoTRMICall;
+import iotruntime.master.CommunicationHandler;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+import iotrmi.Java.IoTRMIObject;
+
+public class TestClass_CBStub implements TestClassInterface {
+
+	/**
+	 * Class Properties
+	 */
+	private IoTRMICall rmiCall;
+	private String address;
+	private int[] ports;
+	private List<CallBackInterface> listCBObj;
+
+	/**
+	 * Class Constants
+	 */
+	private final static int NUM_CB_OBJ = 1;
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
+
+		"voidsetA(int)",
+		"voidsetB(float)",
+		"voidsetC(string)",
+		"sumArray(string[])",
+		"intsetAndGetA(int)",
+		"intsetACAndGetA(string,int)",
+		"intcallBack()",
+		"voidregisterCallBack(CallBackInterface)",
+		"voidregisterCallBack(CallBackInterface[])"
+	};
+
+	/**
+	 * Constructors
+	 */
+	// Assign rmiCall from outside
+	public TestClass_CBStub(IoTRMICall _rmiCall, int _objectId, String _address, int[] _ports) throws IOException {
+
+		address = _address;
+		ports = _ports;
+		objectId = _objectId;
+		rmiCall = _rmiCall;
+	}
+
+
+	/**
+	 * Instantiation of callback objects
+	 */
+	public static int numCallbackObjects() {
+
+		return NUM_CB_OBJ;	// Generated by the IoTCompiler
+	}
+
+
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+
+
+	public void registerCallback(CallBackInterface _cb) {
+
+		Thread thread = new Thread() {
+			public void run() {
+	            try{
+					CallBack_Skeleton cbskel = new CallBack_Skeleton(_cb, ports[0]);
+					cbskel.waitRequestInvokeMethod();
+				} catch (Exception ex){
+					ex.printStackTrace();
+					throw new Error("Error instantiating class CallBack_Skeleton!");
+	            }
+	        }
+	    };
+		thread.start();
+
+		String sign = "voidregisterCallBack(CallBackInterface)";
+		Class<?> retType = void.class;
+		// port, address, and rev
+		Class<?>[] paramCls = new Class<?>[] { int.class, String.class, int.class };
+		Object[] paramObj = new Object[] { ports[0], address, 0 };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	// Multiple callback handling
+	public void registerCallback(CallBackInterface[] _cb) {
+
+		try {
+			for (int objId = 0; objId < _cb.length; objId++) {
+				CallBack_CBSkeleton skel = new CallBack_CBSkeleton(_cb[objId], objId);
+				listCBObj.add(skel);
+			}
+		} catch (	ClassNotFoundException | 
+					InstantiationException |
+					IllegalAccessException |
+					IOException ex) {
+			ex.printStackTrace();
+			throw new Error("Class not found / instantiation / illegal access / IO error!");
+		}
+
+		Thread thread = new Thread() {
+			public void run() {
+	            try{
+					String[] methodSignatures = CallBack_CBSkeleton.getMethodSignatures();
+					IoTRMIObject rmiObj = new IoTRMIObject(ports[0], methodSignatures);
+					Object retObj = null;
+					while (true) {
+						byte[] method = rmiObj.getMethodBytes();
+						int objId = IoTRMIObject.getObjectId(method);
+						CallBack_CBSkeleton skel = (CallBack_CBSkeleton) listCBObj.get(objId);
+						if (skel != null) {
+							rmiObj.setMethodBytes(method);
+							retObj = skel.invokeMethod(rmiObj);
+						}
+						if (retObj != null) {
+							rmiObj.sendReturnObj(retObj);
+						}
+					}
+				} catch (Exception ex){
+					ex.printStackTrace();
+					throw new Error("Error instantiating class CallBack_Skeleton!");
+	            }
+	        }
+	    };
+		thread.start();
+
+		String sign = "voidregisterCallBack(CallBackInterface[])";
+		Class<?> retType = void.class;
+		// port, address, rev, and number of objects
+		Class<?>[] paramCls = new Class<?>[] { int.class, String.class, int.class, int.class };
+		Object[] paramObj = new Object[] { ports[0], address, 0, _cb.length };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	public void setA(int _int) {
+
+		String sign = "voidsetA(int)";
+		Class<?> retType = void.class;
+		Class<?>[] paramCls = new Class<?>[] { int.class };
+		Object[] paramObj = new Object[] { _int };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	public void setB(float _float) {
+
+		String sign = "voidsetB(float)";
+		Class<?> retType = void.class;
+		Class<?>[] paramCls = new Class<?>[] { float.class };
+		Object[] paramObj = new Object[] { _float };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	public void setC(String _string) {
+
+		String sign = "voidsetC(string)";
+		Class<?> retType = void.class;
+		Class<?>[] paramCls = new Class<?>[] { String.class };
+		Object[] paramObj = new Object[] { _string };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	// Getters
+	public String sumArray(String[] newA) {
+
+		String sign = "sumArray(string[])";
+		Class<?> retType = String.class;
+		Class<?>[] paramCls = new Class<?>[] { String[].class };
+		Object[] paramObj = new Object[] { newA };
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+		return (String)retObj;
+	}
+
+
+	public int setAndGetA(int newA) {
+		String sign = "intsetAndGetA(int)";
+		Class<?> retType = int.class;
+		Class<?>[] paramCls = new Class<?>[] { int.class };
+		Object[] paramObj = new Object[] { newA };
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+		return (int)retObj;
+	}
+
+
+	public int setACAndGetA(String newC, int newA) {
+
+		String sign = "intsetACAndGetA(string,int)";
+		Class<?> retType = int.class;
+		Class<?>[] paramCls = new Class<?>[] { String.class, int.class };
+		Object[] paramObj = new Object[] { newC, newA };
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+		return (int)retObj;
+	}
+
+
+	public int callBack() {
+
+		String sign = "intcallBack()";
+		Class<?> retType = int.class;
+		Class<?>[] paramCls = new Class<?>[] { };
+		Object[] paramObj = new Object[] { };
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+		return (int)retObj;
+
+	}
+
+
+	public static void main(String[] args) throws Exception {
+
+	}
+}
+
+
diff --git a/iotjava/iotrmi/Java/sample/TestClass_Skeleton.java b/iotjava/iotrmi/Java/sample/TestClass_Skeleton.java
index 7856cdf..02b279a 100644
--- a/iotjava/iotrmi/Java/sample/TestClass_Skeleton.java
+++ b/iotjava/iotrmi/Java/sample/TestClass_Skeleton.java
@@ -1,14 +1,21 @@
 package iotrmi.Java.sample;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Set;
-import iotrmi.Java.IoTRMIObject;
+import java.util.Map;
+import java.util.HashMap;
 
-import java.util.Arrays;
+import iotrmi.Java.IoTRMIObject;
+import iotrmi.Java.IoTRMICall;
 
 public class TestClass_Skeleton implements TestClassInterface {
 
-	private String[] methodSignatures = {
+	/**
+	 * Class Constants
+	 */
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
 
 		"voidsetA(int)",
 		"voidsetB(float)",
@@ -17,10 +24,12 @@ public class TestClass_Skeleton implements TestClassInterface {
 		"intsetAndGetA(int)",
 		"intsetACAndGetA(string,int)",
 		"intcallBack()",
-		"voidregisterCallBack(CallBackInterface)"
+		"voidregisterCallBack(CallBackInterface)",
+		"voidregisterCallBack(CallBackInterface[])"
 	};
 
-	private TestClass tc;
+	private TestClassInterface tc;
+	private int port;
 	private IoTRMIObject rmiObj;
 	private CallBackInterface cbstub;
 
@@ -28,14 +37,14 @@ public class TestClass_Skeleton implements TestClassInterface {
 	/**
 	 * Constructors
 	 */
-	//public TestClass_Skeleton(Object[] paramObj, int _port) throws
 	public TestClass_Skeleton(TestClass _tc, int _port) throws
 		ClassNotFoundException, InstantiationException,
 			IllegalAccessException, IOException {
 
-		//tc = new TestClass((int)paramObj[0], (float)paramObj[1], (String)paramObj[2]);
 		tc = _tc;
+		port = _port;
 		rmiObj = new IoTRMIObject(_port, methodSignatures);
+		waitRequestInvokeMethod();
 	}
 
 
@@ -45,51 +54,72 @@ public class TestClass_Skeleton implements TestClassInterface {
 		while (true) {
 
 			rmiObj.getMethodBytes();
-			String methodSign = rmiObj.getSignature();
-			Object[] paramObj = null;
-			Object retObj = null;
-			System.out.println("Method sign: " + methodSign);
-
-			if (methodSign.equals("voidsetA(int)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
-					new Class<?>[] { null }, new Class<?>[] { null });
-				setA((int) paramObj[0]);
-			} else if (methodSign.equals("voidsetB(float)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { float.class }, 
-					new Class<?>[] { null }, new Class<?>[] { null });
-				setB((float) paramObj[0]);
-			} else if (methodSign.equals("voidsetC(string)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { String.class }, 
-					new Class<?>[] { null }, new Class<?>[] { null });
-				setC((String) paramObj[0]);
-			} else if (methodSign.equals("sumArray(string[])")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { String[].class }, 
-					new Class<?>[] { null }, new Class<?>[] { null });
-				retObj = sumArray((String[]) paramObj[0]);
-			} else if (methodSign.equals("intsetAndGetA(int)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
-					new Class<?>[] { null }, new Class<?>[] { null });
-				retObj = setAndGetA((int) paramObj[0]);
-			} else if (methodSign.equals("intsetACAndGetA(string,int)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { String.class, int.class }, 
-					new Class<?>[] { null, null }, new Class<?>[] { null, null });
-				retObj = setACAndGetA((String) paramObj[0], (int) paramObj[1]);
-			} else if (methodSign.equals("voidregisterCallBack(CallBackInterface)")) {
-				paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class, String.class, int.class }, 
-					new Class<?>[] { null, null, null }, new Class<?>[] { null, null, null });
-				CallBackInterface cbstub = new CallBack_Stub((int) paramObj[0], (String) paramObj[1], (int) paramObj[2]);
-				registerCallback((CallBackInterface) cbstub);
-			} else if (methodSign.equals("intcallBack()")) {
-				retObj = callBack();
-			} else
-				throw new Error("Signature un-recognized!");
-
-			if (retObj != null) {
-				rmiObj.sendReturnObj(retObj);
+			int _objectId = rmiObj.getObjectId();
+			if (_objectId == objectId) {
+			// Multiplex based on object Id
+				String methodSign = rmiObj.getSignature();
+				Object[] paramObj = null;
+				Object retObj = null;
+
+				if (methodSign.equals("voidsetA(int)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
+						new Class<?>[] { null }, new Class<?>[] { null });
+					setA((int) paramObj[0]);
+				} else if (methodSign.equals("voidsetB(float)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { float.class }, 
+						new Class<?>[] { null }, new Class<?>[] { null });
+					setB((float) paramObj[0]);
+				} else if (methodSign.equals("voidsetC(string)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { String.class }, 
+						new Class<?>[] { null }, new Class<?>[] { null });
+					setC((String) paramObj[0]);
+				} else if (methodSign.equals("sumArray(string[])")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { String[].class }, 
+						new Class<?>[] { null }, new Class<?>[] { null });
+					retObj = sumArray((String[]) paramObj[0]);
+				} else if (methodSign.equals("intsetAndGetA(int)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class }, 
+						new Class<?>[] { null }, new Class<?>[] { null });
+					retObj = setAndGetA((int) paramObj[0]);
+				} else if (methodSign.equals("intsetACAndGetA(string,int)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { String.class, int.class }, 
+						new Class<?>[] { null, null }, new Class<?>[] { null, null });
+					retObj = setACAndGetA((String) paramObj[0], (int) paramObj[1]);
+				} else if (methodSign.equals("voidregisterCallBack(CallBackInterface)")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class, String.class, int.class }, 
+						new Class<?>[] { null, null, null }, new Class<?>[] { null, null, null });
+					CallBackInterface cbstub = new CallBack_Stub((int) paramObj[0], (String) paramObj[1], (int) paramObj[2]);
+					registerCallback((CallBackInterface) cbstub);
+				} else if (methodSign.equals("voidregisterCallBack(CallBackInterface[])")) {
+					paramObj = rmiObj.getMethodParams(new Class<?>[] { int.class, String.class, int.class, int.class }, 
+						new Class<?>[] { null, null, null, null }, new Class<?>[] { null, null, null, null });
+					String[] methodSignatures = CallBack_CBStub.getMethodSignatures();
+					IoTRMICall rmiCall = new IoTRMICall((int) paramObj[0], (String) paramObj[1], (int) paramObj[2], methodSignatures);
+					int numStubs = (int) paramObj[3];
+					CallBackInterface[] stub = new CallBackInterface[numStubs];
+					for (int objId = 0; objId < numStubs; objId++) {
+						stub[objId] = new CallBack_CBStub(rmiCall, objId, (String) paramObj[1]);
+					}
+					registerCallback(stub);
+				} else if (methodSign.equals("intcallBack()")) {
+					retObj = callBack();
+				} else
+					throw new Error("Signature not recognized!");
+
+				if (retObj != null) {
+					rmiObj.sendReturnObj(retObj);
+				}
+				System.out.println("Servicing remote call for object: " + objectId + " - method: " + methodSign);
 			}
-			System.out.println("Servicing remote call for method: " + methodSign);
 		}
 	}
+
+
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
 	
 	
 	public void setA(int _int) {
@@ -132,7 +162,11 @@ public class TestClass_Skeleton implements TestClassInterface {
 		
 		tc.registerCallback(_cb);
 	}
-	
+
+	public void registerCallback(CallBackInterface[] _cb) {
+		
+		tc.registerCallback(_cb);
+	}
 	
 	public int callBack() {
 		
@@ -144,8 +178,53 @@ public class TestClass_Skeleton implements TestClassInterface {
 
 		int port = 5010;
 		TestClass tc = new TestClass(3, 5f, "7911");
-		//TestClass_Skeleton tcSkel = new TestClass_Skeleton(new Object[] { 3, 5f, "7911"}, port);
 		TestClass_Skeleton tcSkel = new TestClass_Skeleton(tc, port);
-		tcSkel.waitRequestInvokeMethod();
+
+/*		String[] methodSignatures = TestClass_CBSkeleton.getMethodSignatures();
+		IoTRMIObject rmiObj = new IoTRMIObject(port, methodSignatures);
+		Map<Integer,TestClassInterface> mapCBObject = new HashMap<Integer,TestClassInterface>();
+
+		// Can replace for-loop with while-loop if necessary
+		for (int i = 1; i < 3; i++) {
+			TestClassInterface tcSkel = new TestClass_CBSkeleton(tc, i);
+			mapCBObject.put(i, tcSkel);
+		}
+
+		Object retObj = null;
+		while (true) {
+			byte[] method = rmiObj.getMethodBytes();
+			int objId = IoTRMIObject.getObjectId(method);
+			TestClass_CBSkeleton tcSkel = (TestClass_CBSkeleton) mapCBObject.get(objId);
+			if (tcSkel != null) {
+				rmiObj.setMethodBytes(method);
+				retObj = tcSkel.invokeMethod(rmiObj);
+			}
+			if (retObj != null) {
+				rmiObj.sendReturnObj(retObj);
+			}
+		}
+*/
+		//int objectId = 1;
+		//System.out.println("Creating 0 object");
+		//TestClass_Skeleton tcSkel1 = new TestClass_Skeleton(tc, rmiObj, objectId);
+		//System.out.println("Creating 1 object");
+		//objectId = 2;
+		//TestClass_Skeleton tcSkel2 = new TestClass_Skeleton(tc, rmiObj, objectId);
+		//System.out.println("Creating 2 object");
+
+		/*for (int i = 1; i < 3; i++) {
+			final int objectId = i;
+			Thread thread = new Thread() {
+				public void run() {
+			        try{
+						TestClass_Skeleton tcSkel = new TestClass_Skeleton(tc, rmiObj, objectId);
+					} catch (Exception ex){
+						ex.printStackTrace();
+						throw new Error("Error instantiating class CallBack_Skeleton!");
+			        }
+			    }
+			};
+			thread.start();
+		}*/
 	}
 }
diff --git a/iotjava/iotrmi/Java/sample/TestClass_Stub.java b/iotjava/iotrmi/Java/sample/TestClass_Stub.java
index 1eb77d4..d7b9d51 100644
--- a/iotjava/iotrmi/Java/sample/TestClass_Stub.java
+++ b/iotjava/iotrmi/Java/sample/TestClass_Stub.java
@@ -5,6 +5,10 @@ import iotrmi.Java.IoTRMICall;
 import iotruntime.master.CommunicationHandler;
 
 import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+import iotrmi.Java.IoTRMIObject;
 
 public class TestClass_Stub implements TestClassInterface {
 
@@ -14,13 +18,14 @@ public class TestClass_Stub implements TestClassInterface {
 	private IoTRMICall rmiCall;
 	private String address;
 	private int[] ports;
+	private List<CallBackInterface> listCBObj;
 
 	/**
 	 * Class Constants
 	 */
 	private final static int NUM_CB_OBJ = 1;
-
-	private String[] methodSignatures = {
+	private int objectId = 0;	// Default value is 0
+	private final static String[] methodSignatures = {
 
 		"voidsetA(int)",
 		"voidsetB(float)",
@@ -29,7 +34,8 @@ public class TestClass_Stub implements TestClassInterface {
 		"intsetAndGetA(int)",
 		"intsetACAndGetA(string,int)",
 		"intcallBack()",
-		"voidregisterCallBack(CallBackInterface)"
+		"voidregisterCallBack(CallBackInterface)",
+		"voidregisterCallBack(CallBackInterface[])"
 	};
 
 	/**
@@ -40,6 +46,16 @@ public class TestClass_Stub implements TestClassInterface {
 		address = _address;
 		ports = _ports;
 		rmiCall = new IoTRMICall(_port, _address, _rev, methodSignatures);
+		listCBObj = new ArrayList<CallBackInterface>();
+	}
+
+	// Assign rmiCall from outside
+	public TestClass_Stub(IoTRMICall _rmiCall, int _objectId, String _address, int[] _ports) throws IOException {
+
+		address = _address;
+		ports = _ports;
+		objectId = _objectId;
+		rmiCall = _rmiCall;
 	}
 
 
@@ -52,10 +68,15 @@ public class TestClass_Stub implements TestClassInterface {
 	}
 
 
-	public void registerCallback(CallBackInterface _cb) {
+	// Return method signatures
+	public static String[] getMethodSignatures() {
+
+		return methodSignatures;
+	}
+
 
-		//int port = 5011;	// Send this info to the other end to start the stub
-		//String address = "localhost";
+	// Single callback handling
+	public void registerCallback(CallBackInterface _cb) {
 
 		Thread thread = new Thread() {
 			public void run() {
@@ -75,7 +96,58 @@ public class TestClass_Stub implements TestClassInterface {
 		// port, address, and rev
 		Class<?>[] paramCls = new Class<?>[] { int.class, String.class, int.class };
 		Object[] paramObj = new Object[] { ports[0], address, 0 };
-		rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
+	}
+
+
+	// Multiple callback handling
+	public void registerCallback(CallBackInterface[] _cb) {
+
+		try {
+			for (int objId = 0; objId < _cb.length; objId++) {
+				CallBack_CBSkeleton skel = new CallBack_CBSkeleton(_cb[objId], objId);
+				listCBObj.add(skel);
+			}
+		} catch (	ClassNotFoundException | 
+					InstantiationException |
+					IllegalAccessException |
+					IOException ex) {
+			ex.printStackTrace();
+			throw new Error("Class not found / instantiation / illegal access / IO error!");
+		}
+
+		Thread thread = new Thread() {
+			public void run() {
+	            try{
+					String[] methodSignatures = CallBack_CBSkeleton.getMethodSignatures();
+					IoTRMIObject rmiObj = new IoTRMIObject(ports[0], methodSignatures);
+					Object retObj = null;
+					while (true) {
+						byte[] method = rmiObj.getMethodBytes();
+						int objId = IoTRMIObject.getObjectId(method);
+						CallBack_CBSkeleton skel = (CallBack_CBSkeleton) listCBObj.get(objId);
+						if (skel != null) {
+							rmiObj.setMethodBytes(method);
+							retObj = skel.invokeMethod(rmiObj);
+						}
+						if (retObj != null) {
+							rmiObj.sendReturnObj(retObj);
+						}
+					}
+				} catch (Exception ex){
+					ex.printStackTrace();
+					throw new Error("Error instantiating class CallBack_Skeleton!");
+	            }
+	        }
+	    };
+		thread.start();
+
+		String sign = "voidregisterCallBack(CallBackInterface[])";
+		Class<?> retType = void.class;
+		// port, address, rev, and number of objects
+		Class<?>[] paramCls = new Class<?>[] { int.class, String.class, int.class, int.class };
+		Object[] paramObj = new Object[] { ports[0], address, 0, _cb.length };
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 	}
 
 
@@ -85,7 +157,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = void.class;
 		Class<?>[] paramCls = new Class<?>[] { int.class };
 		Object[] paramObj = new Object[] { _int };
-		rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 	}
 
 
@@ -95,7 +167,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = void.class;
 		Class<?>[] paramCls = new Class<?>[] { float.class };
 		Object[] paramObj = new Object[] { _float };
-		rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 	}
 
 
@@ -105,7 +177,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = void.class;
 		Class<?>[] paramCls = new Class<?>[] { String.class };
 		Object[] paramObj = new Object[] { _string };
-		rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 	}
 
 
@@ -116,7 +188,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = String.class;
 		Class<?>[] paramCls = new Class<?>[] { String[].class };
 		Object[] paramObj = new Object[] { newA };
-		Object retObj = rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 		return (String)retObj;
 	}
 
@@ -126,7 +198,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = int.class;
 		Class<?>[] paramCls = new Class<?>[] { int.class };
 		Object[] paramObj = new Object[] { newA };
-		Object retObj = rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 		return (int)retObj;
 	}
 
@@ -137,7 +209,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = int.class;
 		Class<?>[] paramCls = new Class<?>[] { String.class, int.class };
 		Object[] paramObj = new Object[] { newC, newA };
-		Object retObj = rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 		return (int)retObj;
 	}
 
@@ -148,7 +220,7 @@ public class TestClass_Stub implements TestClassInterface {
 		Class<?> retType = int.class;
 		Class<?>[] paramCls = new Class<?>[] { };
 		Object[] paramObj = new Object[] { };
-		Object retObj = rmiCall.remoteCall(sign, retType, null, null, paramCls, paramObj);
+		Object retObj = rmiCall.remoteCall(objectId, sign, retType, null, null, paramCls, paramObj);
 		return (int)retObj;
 
 	}
@@ -160,20 +232,25 @@ public class TestClass_Stub implements TestClassInterface {
 		int numOfPorts = TestClass_Stub.numCallbackObjects();
 		int[] ports = comHan.getCallbackPorts(numOfPorts);
 
-		System.out.println("Allocated ports: " + Arrays.toString(ports));
-
 		int port = 5010;
 		String address = "localhost";
 		int rev = 0;
 
+		System.out.println("Allocated ports: " + Arrays.toString(ports));
+
 		TestClass_Stub tcstub = new TestClass_Stub(port, address, rev, ports);
 		System.out.println("Return value: " + tcstub.setAndGetA(123));
 		System.out.println("Return value: " + tcstub.setACAndGetA("string", 123));
 		System.out.println("Return value: " + tcstub.sumArray(new String[] { "123", "456", "987" }));
 
-		/*CallBack cb = new CallBack(23);
+		CallBackInterface cb1 = new CallBack(23);
+		CallBackInterface cb2 = new CallBack(33);
+		CallBackInterface cb3 = new CallBack(43);
+		CallBackInterface[] cb = { cb1, cb2, cb3 };
 		tcstub.registerCallback(cb);
-		System.out.println("Return value from callback: " + tcstub.callBack());*/
-		//System.out.println("Return value: " + tcstub.setAndGetA(1234));
+		System.out.println("Return value from callback: " + tcstub.callBack());
+
 	}
 }
+
+
-- 
2.34.1