diff --git a/src/scala/microsoft-spark-2-3/pom.xml b/src/scala/microsoft-spark-2-3/pom.xml
index 8cde04b6e..4df5b0c22 100644
--- a/src/scala/microsoft-spark-2-3/pom.xml
+++ b/src/scala/microsoft-spark-2-3/pom.xml
@@ -45,6 +45,12 @@
1.2.5
test
+
+ com.microsoft.scala
+ microsoft-spark-common
+ ${microsoft-spark.version}
+ compile
+
@@ -72,6 +78,24 @@
+
+ maven-assembly-plugin
+ 3.1.1
+
+ false
+
+ jar-with-dependencies
+
+
+
+
+ package
+
+ single
+
+
+
+
diff --git a/src/scala/microsoft-spark-2-4/pom.xml b/src/scala/microsoft-spark-2-4/pom.xml
index 1c959bb05..efb82a6c1 100644
--- a/src/scala/microsoft-spark-2-4/pom.xml
+++ b/src/scala/microsoft-spark-2-4/pom.xml
@@ -45,6 +45,12 @@
1.2.5
test
+
+ com.microsoft.scala
+ microsoft-spark-common
+ ${microsoft-spark.version}
+ compile
+
@@ -72,6 +78,24 @@
+
+ maven-assembly-plugin
+ 3.1.1
+
+ false
+
+ jar-with-dependencies
+
+
+
+
+ package
+
+ single
+
+
+
+
diff --git a/src/scala/microsoft-spark-2-4/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala b/src/scala/microsoft-spark-2-4/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
deleted file mode 100644
index aceb58c01..000000000
--- a/src/scala/microsoft-spark-2-4/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the .NET Foundation under one or more agreements.
- * The .NET Foundation licenses this file to you under the MIT license.
- * See the LICENSE file in the project root for more information.
- */
-
-package org.apache.spark.api.dotnet
-
-import scala.collection.mutable.HashMap
-
-/**
- * Tracks JVM objects returned to .NET which is useful for invoking calls from .NET on JVM objects.
- */
-private[dotnet] class JVMObjectTracker {
-
- // Multiple threads may access objMap and increase objCounter. Because get method return Option,
- // it is convenient to use a Scala map instead of java.util.concurrent.ConcurrentHashMap.
- private[this] val objMap = new HashMap[String, Object]
- private[this] var objCounter: Int = 1
-
- def getObject(id: String): Object = {
- synchronized {
- objMap(id)
- }
- }
-
- def get(id: String): Option[Object] = {
- synchronized {
- objMap.get(id)
- }
- }
-
- def put(obj: Object): String = {
- synchronized {
- val objId = objCounter.toString
- objCounter = objCounter + 1
- objMap.put(objId, obj)
- objId
- }
- }
-
- def remove(id: String): Option[Object] = {
- synchronized {
- objMap.remove(id)
- }
- }
-
- def clear(): Unit = {
- synchronized {
- objMap.clear()
- objCounter = 1
- }
- }
-}
diff --git a/src/scala/microsoft-spark-3-0/pom.xml b/src/scala/microsoft-spark-3-0/pom.xml
index 9ece1794f..c52d2c6b7 100644
--- a/src/scala/microsoft-spark-3-0/pom.xml
+++ b/src/scala/microsoft-spark-3-0/pom.xml
@@ -45,6 +45,12 @@
1.2.5
test
+
+ com.microsoft.scala
+ microsoft-spark-common
+ ${microsoft-spark.version}
+ compile
+
@@ -72,6 +78,24 @@
+
+ maven-assembly-plugin
+ 3.1.1
+
+ false
+
+ jar-with-dependencies
+
+
+
+
+ package
+
+ single
+
+
+
+
diff --git a/src/scala/microsoft-spark-3-0/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala b/src/scala/microsoft-spark-3-0/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
deleted file mode 100644
index 81cfaf88b..000000000
--- a/src/scala/microsoft-spark-3-0/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the .NET Foundation under one or more agreements.
- * The .NET Foundation licenses this file to you under the MIT license.
- * See the LICENSE file in the project root for more information.
- */
-
-
-package org.apache.spark.api.dotnet
-
-import scala.collection.mutable.HashMap
-
-/**
- * Tracks JVM objects returned to .NET which is useful for invoking calls from .NET on JVM objects.
- */
-private[dotnet] class JVMObjectTracker {
-
- // Multiple threads may access objMap and increase objCounter. Because get method return Option,
- // it is convenient to use a Scala map instead of java.util.concurrent.ConcurrentHashMap.
- private[this] val objMap = new HashMap[String, Object]
- private[this] var objCounter: Int = 1
-
- def getObject(id: String): Object = {
- synchronized {
- objMap(id)
- }
- }
-
- def get(id: String): Option[Object] = {
- synchronized {
- objMap.get(id)
- }
- }
-
- def put(obj: Object): String = {
- synchronized {
- val objId = objCounter.toString
- objCounter = objCounter + 1
- objMap.put(objId, obj)
- objId
- }
- }
-
- def remove(id: String): Option[Object] = {
- synchronized {
- objMap.remove(id)
- }
- }
-
- def clear(): Unit = {
- synchronized {
- objMap.clear()
- objCounter = 1
- }
- }
-}
diff --git a/src/scala/microsoft-spark-common/pom.xml b/src/scala/microsoft-spark-common/pom.xml
new file mode 100644
index 000000000..1f256f157
--- /dev/null
+++ b/src/scala/microsoft-spark-common/pom.xml
@@ -0,0 +1,56 @@
+
+
+ 4.0.0
+
+ com.microsoft.scala
+ microsoft-spark
+ ${microsoft-spark.version}
+
+ microsoft-spark-common
+ 2019
+
+ UTF-8
+ 2.12.8
+ 2.12
+
+
+
+
+
+ org.scala-lang
+ scala-library
+ ${scala.version}
+
+
+
+
+ src/main/scala
+ src/test/scala
+
+
+ org.scala-tools
+ maven-scala-plugin
+ 2.15.2
+
+
+
+ compile
+ testCompile
+
+
+
+
+ ${scala.version}
+
+ -target:jvm-1.8
+ -deprecation
+ -feature
+
+
+
+
+
+
+
diff --git a/src/scala/microsoft-spark-2-3/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala b/src/scala/microsoft-spark-common/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
similarity index 96%
rename from src/scala/microsoft-spark-2-3/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
rename to src/scala/microsoft-spark-common/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
index aceb58c01..b5290cb49 100644
--- a/src/scala/microsoft-spark-2-3/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
+++ b/src/scala/microsoft-spark-common/src/main/scala/org/apache/spark/api/dotnet/JVMObjectTracker.scala
@@ -11,7 +11,7 @@ import scala.collection.mutable.HashMap
/**
* Tracks JVM objects returned to .NET which is useful for invoking calls from .NET on JVM objects.
*/
-private[dotnet] class JVMObjectTracker {
+class JVMObjectTracker {
// Multiple threads may access objMap and increase objCounter. Because get method return Option,
// it is convenient to use a Scala map instead of java.util.concurrent.ConcurrentHashMap.
diff --git a/src/scala/pom.xml b/src/scala/pom.xml
index bb0b408ae..56c661af6 100644
--- a/src/scala/pom.xml
+++ b/src/scala/pom.xml
@@ -14,6 +14,7 @@
microsoft-spark-2-3
microsoft-spark-2-4
microsoft-spark-3-0
+ microsoft-spark-common