You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by GitBox <gi...@apache.org> on 2018/11/13 22:20:48 UTC

[GitHub] lanking520 closed pull request #13253: Rc cherry pick

lanking520 closed pull request #13253: Rc cherry pick
URL: https://github.com/apache/incubator-mxnet/pull/13253
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/Makefile b/Makefile
index a4b41b8d837..fe2df2c20af 100644
--- a/Makefile
+++ b/Makefile
@@ -606,7 +606,7 @@ scalaclean:
 
 scalapkg:
 	(cd $(ROOTDIR)/scala-package; \
-		mvn package -P$(SCALA_PKG_PROFILE),$(SCALA_VERSION_PROFILE) -Dcxx="$(CXX)" \
+		mvn package -P$(SCALA_PKG_PROFILE),$(SCALA_VERSION_PROFILE),integrationtest -Dcxx="$(CXX)" \
 		    -Dbuild.platform="$(SCALA_PKG_PROFILE)" \
 			-Dcflags="$(CFLAGS)" -Dldflags="$(LDFLAGS)" \
 			-Dcurrent_libdir="$(ROOTDIR)/lib" \
diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md
index ef7306c1495..3c1841c19bd 100644
--- a/docs/tutorials/index.md
+++ b/docs/tutorials/index.md
@@ -154,6 +154,12 @@ Select API:&nbsp;
 * [MXNet-Scala  Examples](https://github.com/apache/incubator-mxnet/tree/master/scala-package/examples/src/main/scala/org/apache/mxnetexamples)
 <hr>
 
+## Java Tutorials
+* Getting Started
+    * [Developer Environment Setup on IntelliJ IDE](/tutorials/java/mxnet_java_on_intellij.html)
+* [MXNet-Java  Examples](https://github.com/apache/incubator-mxnet/tree/master/scala-package/examples/src/main/java/org/apache/mxnetexamples)
+<hr>
+
 ## C++ Tutorials
 
 * Models
diff --git a/docs/tutorials/java/mxnet_java_on_intellij.md b/docs/tutorials/java/mxnet_java_on_intellij.md
new file mode 100644
index 00000000000..b90a92b0a7b
--- /dev/null
+++ b/docs/tutorials/java/mxnet_java_on_intellij.md
@@ -0,0 +1,210 @@
+# Run MXNet Java Examples Using the IntelliJ IDE (macOS)
+
+This tutorial guides you through setting up a simple Java project in IntelliJ IDE on macOS and demonstrates usage of the MXNet Java APIs. 
+
+## Prerequisites:
+To use this tutorial you need the following pre-requisites:
+
+- [Java 8 JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
+- [Maven](https://maven.apache.org/install.html)
+- [OpenCV](https://opencv.org/)
+- [IntelliJ IDEA](https://www.jetbrains.com/idea/) (One can download the community edition from [here](https://www.jetbrains.com/idea/download))
+
+### MacOS Prerequisites
+
+**Step 1.** Install brew:
+```
+/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+```
+
+Or, if you already have brew, update it:
+```
+brew update
+```
+
+**Step 2.** Install Java 8:
+```
+brew tap caskroom/versions
+brew cask install java8
+```
+
+**Step 3.** Install maven:
+```
+brew install maven
+```
+
+**Step 4.** Install OpenCV:
+```
+brew install opencv
+```
+
+You can also run this tutorial on an Ubuntu machine after installing the following prerequisites.
+### Ubuntu Prerequisites
+
+**Step 1.** Download the MXNet source.
+
+```bash
+git clone --recursive https://github.com/apache/incubator-mxnet.git mxnet
+cd mxnet
+```
+
+**Step 2.** Run the dependency installation scripts.
+
+```bash
+sudo ./ci/docker/install/ubuntu_core.sh
+sudo ./ci/docker/install/ubuntu_scala.sh
+```
+
+The `ubuntu_scala.sh` installs the common dependencies required for both MXNet Scala and MXNet Java packages.
+
+## Set Up Your Project
+
+**Step 1.** Install and setup [IntelliJ IDEA](https://www.jetbrains.com/idea/)
+
+**Step 2.** Create a new Project:
+
+![intellij welcome](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/scala/intellij-welcome.png)
+
+From the IntelliJ welcome screen, select "Create New Project".
+
+Choose the Maven project type. 
+
+Select the checkbox for `Create from archetype`, then choose `org.apache.maven.archetypes:maven-archetype-quickstart` from the list below. More on this can be found on a Maven tutorial : [Maven in 5 Minutes](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html). 
+
+![maven project type - archetype](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/project-archetype.png)
+
+click `Next`.
+
+![project metadata](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/intellij-project-metadata.png)
+
+Set the project's metadata. For this tutorial, use the following:
+
+**GroupId**
+```
+mxnet
+```
+**ArtifactId**
+```
+ArtifactId: javaMXNet
+```
+**Version**
+```
+1.0-SNAPSHOT
+```
+
+TODO
+![project properties](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/intellij-project-properties.png)
+
+Review the project's properties. The settings can be left as their default.
+
+TODO
+![project location](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/intellij-project-location.png)
+
+Set the project's location. The rest of the settings can be left as their default.
+
+TODO
+![project 1](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/intellij-project-pom.png)
+
+After clicking Finish, you will be presented with the project's first view.
+The project's `pom.xml` will be open for editing.
+
+**Step 3.** Add the following Maven dependency to your `pom.xml` file under the `dependencies` tag: 
+ 
+```html
+<dependency>
+  <groupId>org.apache.mxnet</groupId>
+  <artifactId>mxnet-full_2.11-osx-x86_64-cpu</artifactId>
+  <version>1.4.0</version>
+</dependency>
+```
+
+To view the latest MXNet Maven packages, you can check [MXNet Maven package repository](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.mxnet%22)
+
+
+**Step 4.** Import dependencies with Maven:
+
+  - Note the prompt in the lower right corner that states "Maven projects need to be imported". If this is not visible, click on the little greed balloon that appears in the lower right corner.
+
+![import_dependencies](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/project-import-changes.png)
+
+Click "Import Changes" in this prompt.
+
+**Step 5.** Build the project:
+- To build the project, from the menu choose Build, and then choose Build Project.
+
+**Step 6.** Navigate to the App.java class in the project and paste the following code, overwriting the original hello world code.
+```java
+package mxnet;
+
+import org.apache.mxnet.javaapi.Context;
+import org.apache.mxnet.javaapi.NDArray;
+
+public class App 
+{
+    public static void main( String[] args )
+    {
+        NDArray nd = NDArray.ones(Context.cpu(), new int[] {10, 20});
+        System.out.println( "Testing MXNet by generating a 10x20 NDArray" );
+        System.out.println("Shape of NDArray is : " + nd.shape());
+    }
+}
+``` 
+ 
+**Step 7.** Now run the App.java by clicking the green arrow as highlighted in the image below.
+
+![run hello mxnet](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/java/intellij-run-projects.png)
+
+
+The result should be this output:
+
+```
+Testing MXNet by generating a 10x20 NDArray
+Shape of NDArray is : (10,20)
+
+Process finished with exit code 0
+```
+
+
+### Troubleshooting
+
+If you get an error, check the dependencies at the beginning of this tutorial. For example, you might see the following in the middle of the error messages, where `x.x` would the version it's looking for.
+
+```
+...
+Library not loaded: /usr/local/opt/opencv/lib/libopencv_calib3d.x.x.dylib
+...
+```
+
+This can be resolved be installing OpenCV.
+
+
+### Command Line Build Option
+
+- You can also compile the project by using the following command at the command line. Change directories to this project's root folder then run the following:
+
+```bash
+mvn clean install dependency:copy-dependencies
+```
+If the command succeeds, you should see a lot of info and some warning messages, followed by:
+
+```bash
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 3.475 s
+[INFO] Finished at: 2018-11-08T05:06:31-08:00
+[INFO] ------------------------------------------------------------------------
+```
+The build generates a new jar file in the `target` folder called `javaMXNet-1.0-SNAPSHOT.jar`.
+
+To run the App.java use the following command from the project's root folder and you should see the same output as we got when the project was run from IntelliJ.
+```bash
+java -cp target/javaMXNet-1.0-SNAPSHOT.jar:target/dependency/* mxnet.App
+```
+
+## Next Steps
+For more information about MXNet Java resources, see the following:
+
+* [Java Inference API](https://mxnet.incubator.apache.org/api/java/infer.html)
+* [Java Inference Examples](https://github.com/apache/incubator-mxnet/tree/java-api/scala-package/examples/src/main/java/org/apache/mxnetexamples/infer/)
+* [MXNet Tutorials Index](http://mxnet.io/tutorials/index.html)
diff --git a/docs/tutorials/scala/mxnet_java_install_and_run_examples.md b/docs/tutorials/scala/mxnet_java_install_and_run_examples.md
new file mode 100644
index 00000000000..83e1ec5b2da
--- /dev/null
+++ b/docs/tutorials/scala/mxnet_java_install_and_run_examples.md
@@ -0,0 +1,123 @@
+# Install and run Java Examples
+
+## Prerequisites:
+Please follow the Step 1 in the [Scala configuration](http://mxnet.incubator.apache.org/install/scala_setup.html#setup-instructions)
+These should help you install the correct Java version and all dependencies.
+
+## Run the Java example project
+We have provided a general MXNet Java template under `scala-package/mxnet-demo/java-demo` which contains the necessary project files for you to get started. It contains a simple Hello world! equivalent program `JavaSample.java` and a full fledged `ObjectDetection.java `that shows how to run Object Detection on images using MXNet and pre-trained SSD model.
+
+Alternatively you could build project from scratch following the below instructions.
+
+## Import and run the Java package
+For users using a desktop/laptop, we recommend using IntelliJ IDE as it is tested and supported to provide the necessary documentation for the Java API.
+
+Alternatively, users can follow the second instruction to set up an empty Maven project for Java.
+
+### IntelliJ instruction
+If you are using a computer with Ubuntu16.04 or Mac, you can install IntelliJ to run the Java package. Please follow the instruction below:
+
+1. Create a new Java project in IntelliJ. Fire up IntelliJ and click `Create New Project`.
+
+2. Click `Next`, and in the `Create project from template` window, do not select anything and click `Next` again.
+
+3. In the next window choose your `Project name` and the `Project location` and click on `Finish`.
+
+4. Let's add the Java Inference API jars that we build from source. At the top of the window, Go to the `File -> Project Structure`. In the popup window that opens up, click on `Libraries -> +` and select the path to the jar files downloaded. Click `Apply` and then click `OK`.
+
+6. Create a new Java class under the folder `your-project-name/src`. Let's call this class `JavaSample.java`. Type in the following code snippet and run it. In this code snippet, we create an NDArray object in Java and print its shape.
+```java
+import org.apache.mxnet.javaapi.Context;
+import org.apache.mxnet.javaapi.NDArray;
+
+public class JavaSample {
+public static void main(String[] args) {
+  System.out.println("Hello");
+  NDArray nd = NDArray.ones(Context.cpu(), new int[] {10, 20});
+
+  System.out.println("Shape of NDarray is : "  + nd.shape());
+}
+}
+```
+
+7. If all went well, you should see an output like this :
+```
+Hello
+SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
+SLF4J: Defaulting to no-operation (NOP) logger implementation
+SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
+Shape of NDarray is : (10,20)
+Process finished with exit code 0
+```
+This means you have successfully set it up on your machine
+
+### Run the project manually in Maven
+In this example, Maven is being used to create the project. This tutorial referred the [Maven in 5 min](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html) tutorial.
+
+1. Create a new folder and run the following commands
+```
+mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
+```
+You can specify the `groupId` and `artifactId` to your favourite names. You can also create a maven project using empty archetype.
+
+2. then go to `pom.xml` file in your project folder and add the following content.
+
+- Change the `osx-x86_64` to `linux-x86_64` if your platform is linux.
+- Change `cpu` into `gpu` if you are using gpu
+- Change the version of your package from `1.3.1-SNAPSHOT` to the matched jar version.
+```xml
+<dependency>
+  <groupId>org.apache.mxnet</groupId>
+  <artifactId>mxnet-full_2.11-osx-x86_64-cpu</artifactId>
+  <version>1.3.1-SNAPSHOT</version>
+  <scope>system</scope>
+  <systemPath>path-to-your-jar/jarName.jar</systemPath>
+</dependency>
+<dependency>
+  <groupId>args4j</groupId>
+  <artifactId>args4j</artifactId>
+  <version>2.0.29</version>
+  </dependency>
+<dependency>
+  <groupId>org.slf4j</groupId>
+  <artifactId>slf4j-api</artifactId>
+  <version>1.7.7</version>
+</dependency>
+<dependency>
+  <groupId>org.slf4j</groupId>
+  <artifactId>slf4j-log4j12</artifactId>
+  <version>1.7.7</version>
+</dependency>
+```
+3. Finally you can replace the code in `App.java`
+```java
+import org.apache.mxnet.javaapi.Context;
+import org.apache.mxnet.javaapi.NDArray;
+
+public class App {
+public static void main(String[] args) {
+  System.out.println("Hello");
+  NDArray nd = NDArray.ones(Context.cpu(), new int[] {10, 20});
+
+  System.out.println("Shape of NDarray is : "  + nd.shape());
+
+}
+}
+```
+make the package by
+```
+mvn package
+```
+
+and run it by
+```
+java -cp target/my-app-1.0-SNAPSHOT.jar:<full-path-to-jar>/<jarName>.jar com.mycompany.app.App
+```
+The result looks like this:
+```
+Hello
+SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
+SLF4J: Defaulting to no-operation (NOP) logger implementation
+SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
+Shape of NDarray is : (10,20)
+```
\ No newline at end of file
diff --git a/scala-package/core/pom.xml b/scala-package/core/pom.xml
index ea3a2d68c9f..d5396dab1e6 100644
--- a/scala-package/core/pom.xml
+++ b/scala-package/core/pom.xml
@@ -86,7 +86,10 @@
         <artifactId>maven-surefire-plugin</artifactId>
         <version>2.22.0</version>
         <configuration>
-          <skipTests>false</skipTests>
+          <argLine>
+            -Djava.library.path=${project.parent.basedir}/native/${platform}/target
+          </argLine>
+          <skipTests>${skipTests}</skipTests>
         </configuration>
       </plugin>
       <plugin>
@@ -123,5 +126,12 @@
       <artifactId>commons-io</artifactId>
       <version>2.1</version>
     </dependency>
+    <!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <version>1.10.19</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/Executor.scala b/scala-package/core/src/main/scala/org/apache/mxnet/Executor.scala
index fc791d5cd9a..19fb6fe5cee 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/Executor.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/Executor.scala
@@ -45,7 +45,7 @@ object Executor {
  * @see Symbol.bind : to create executor
  */
 class Executor private[mxnet](private[mxnet] val handle: ExecutorHandle,
-                              private[mxnet] val symbol: Symbol) extends WarnIfNotDisposed {
+                              private[mxnet] val symbol: Symbol) extends NativeResource {
   private[mxnet] var argArrays: Array[NDArray] = null
   private[mxnet] var gradArrays: Array[NDArray] = null
   private[mxnet] var auxArrays: Array[NDArray] = null
@@ -59,14 +59,15 @@ class Executor private[mxnet](private[mxnet] val handle: ExecutorHandle,
   private[mxnet] var _group2ctx: Map[String, Context] = null
   private val logger: Logger = LoggerFactory.getLogger(classOf[Executor])
 
-  private var disposed = false
-  protected def isDisposed = disposed
-
-  def dispose(): Unit = {
-    if (!disposed) {
-      outputs.foreach(_.dispose())
-      _LIB.mxExecutorFree(handle)
-      disposed = true
+  override def nativeAddress: CPtrAddress = handle
+  override def nativeDeAllocator: (CPtrAddress => Int) = _LIB.mxExecutorFree
+  // cannot determine the off-heap size of this object
+  override val bytesAllocated: Long = 0
+  override val ref: NativeResourceRef = super.register()
+  override def dispose(): Unit = {
+    if (!super.isDisposed) {
+      super.dispose()
+      outputs.foreach(o => o.dispose())
     }
   }
 
@@ -305,4 +306,5 @@ class Executor private[mxnet](private[mxnet] val handle: ExecutorHandle,
     checkCall(_LIB.mxExecutorPrint(handle, str))
     str.value
   }
+
 }
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/FeedForward.scala b/scala-package/core/src/main/scala/org/apache/mxnet/FeedForward.scala
index 00a1450089f..2ed9d8cfbb8 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/FeedForward.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/FeedForward.scala
@@ -17,9 +17,10 @@
 
 package org.apache.mxnet
 
+import org.apache.mxnet.Base.CPtrAddress
 import org.apache.mxnet.io.NDArrayIter
 import org.apache.mxnet.optimizer.SGD
-import org.slf4j.{LoggerFactory, Logger}
+import org.slf4j.{Logger, LoggerFactory}
 
 import scala.collection.mutable.ListBuffer
 
@@ -55,7 +56,7 @@ class FeedForward private(
     argParams: Map[String, NDArray],
     auxParams: Map[String, NDArray],
     private val allowExtraParams: Boolean,
-    val beginEpoch: Int) {
+    val beginEpoch: Int) extends NativeResource {
 
   val logger: Logger = LoggerFactory.getLogger(classOf[FeedForward])
   private var argumentChecked = false
@@ -126,6 +127,8 @@ class FeedForward private(
   }
 
   // Initialize weight parameters and auxiliary states
+  // The NDArrays associated with the _argParms and _auxParams are not disposed instead
+  // they are passed a outer scope if available.
   private def initParams(inputShapes: Map[String, Shape], overwrite: Boolean = false)
   : (IndexedSeq[String], IndexedSeq[String], IndexedSeq[String]) = {
     val (argShapes, _, auxShapes) = symbol.inferShape(inputShapes)
@@ -137,16 +140,26 @@ class FeedForward private(
     val paramNameShapes = (argNames zip argShapes).filter { case (name, _) =>
       paramNames.contains(name)
     }
-    val argParams = paramNameShapes.map { case (name, shape) =>
-      (name, NDArray.zeros(shape))
+    val argParams = paramNameShapes.map { case (name, shape) => {
+        val param = NDArray.zeros(shape)
+        val curScope = ResourceScope.getCurrentScope()
+        if (curScope.isDefined) curScope.get.moveToOuterScope(param)
+        (name, param)
+      }
     }.toMap
-    val auxParams = (auxNames zip auxShapes).map { case (name, shape) =>
-      (name, NDArray.zeros(shape))
+
+    val auxParams = (auxNames zip auxShapes).map { case (name, shape) => {
+        val param = NDArray.zeros(shape)
+        val curScope = ResourceScope.getCurrentScope()
+        if (curScope.isDefined) curScope.get.moveToOuterScope(param)
+        (name, param)
+      }
     }.toMap
 
     for ((k, v) <- argParams) {
       if (_argParams != null && _argParams.contains(k) && (!overwrite)) {
         argParams(k).set(_argParams(k))
+
       } else {
         initializer(k, v)
       }
@@ -277,13 +290,15 @@ class FeedForward private(
   def fit(trainData: DataIter, evalData: DataIter, evalMetric: EvalMetric, kvStoreType: String,
           epochEndCallback: EpochEndCallback, batchEndCallback: BatchEndCallback,
           logger: Logger, workLoadList: Seq[Float]): Unit = {
-    // init params first to allow kv store use _argParams to decide its type
-    initSymbolParams(trainData)
-    // create kvstore
-    val (kvStore, updateOnKVStore) = Model.createKVStore(kvStoreType, ctx.length, _argParams)
-    fit(trainData, evalData, evalMetric, kvStore, updateOnKVStore,
-      epochEndCallback, batchEndCallback, logger, workLoadList)
-    kvStore.foreach(_.dispose())
+    ResourceScope.using() {
+      // init params first to allow kv store use _argParams to decide its type
+      initSymbolParams(trainData)
+      // create kvstore
+      val (kvStore, updateOnKVStore) = Model.createKVStore(kvStoreType, ctx.length, _argParams)
+      fit(trainData, evalData, evalMetric, kvStore, updateOnKVStore,
+        epochEndCallback, batchEndCallback, logger, workLoadList)
+//      kvStore.foreach(_.dispose())
+    }
   }
 
   def fit(trainData: DataIter, evalData: DataIter, evalMetric: EvalMetric,
@@ -313,11 +328,13 @@ class FeedForward private(
           batchEndCallback: BatchEndCallback, logger: Logger,
           workLoadList: Seq[Float]): Unit = {
     // init params first to allow kv store use _argParams to decide its type
-    initSymbolParams(trainData)
-    // create kvstore
-    val (kvStore, updateOnKVStore) = Model.createKVStore(kv)
-    fit(trainData, evalData, evalMetric, kvStore, updateOnKVStore,
-      epochEndCallback, batchEndCallback, logger, workLoadList)
+    ResourceScope.using() {
+      initSymbolParams(trainData)
+      // create kvstore
+      val (kvStore, updateOnKVStore) = Model.createKVStore(kv)
+      fit(trainData, evalData, evalMetric, kvStore, updateOnKVStore,
+        epochEndCallback, batchEndCallback, logger, workLoadList)
+    }
   }
 
   def fit(trainData: DataIter, evalData: DataIter, evalMetric: EvalMetric,
@@ -352,44 +369,49 @@ class FeedForward private(
                   batchEndCallback: BatchEndCallback = null, logger: Logger = FeedForward.logger,
                   workLoadList: Seq[Float] = null): Unit = {
     require(evalMetric != null, "evalMetric cannot be null")
-    val (argNames, paramNames, auxNames) = initSymbolParams(trainData)
-
-    // init optimizer
-    val batchSizeMultiplier = kvStore.map { kv =>
-      if (kv.`type` == "dist_sync") {
-        kv.numWorkers
-      } else {
-        1
-      }
-    }
-    val batchSize = trainData.batchSize * batchSizeMultiplier.getOrElse(1)
-    this.optimizer.setArgNames(argNames)
-    this.optimizer.setRescaleGrad(1f / batchSize)
-    this.optimizer.setSymbol(this.symbol)
-    val paramIdx2Name =
-      if (updateOnKVStore) {
-        paramNames.zipWithIndex.map { case (name, idx) => idx -> name }.toMap
-      } else {
-        paramNames.zipWithIndex.flatMap { case (name, idx) =>
-          (0 until ctx.length).map(k => (idx * ctx.length + k) -> name).toMap
-        }.toMap
+    // TODO: https://issues.apache.org/jira/browse/MXNET-1171
+    // this leaks memory, initSymbolParams->initParams is already called which allocates
+    // NDArray in argParams, auxParams and here we are overwriting it by calling again.
+    // PhantomRef should take care of releasing this when GC is called, however we have to
+    // wait for the GC call to happen.
+      val (argNames, paramNames, auxNames) = initSymbolParams(trainData)
+
+      // init optimizer
+      val batchSizeMultiplier = kvStore.map { kv =>
+        if (kv.`type` == "dist_sync") {
+          kv.numWorkers
+        } else {
+          1
+        }
       }
-    this.optimizer.setIdx2Name(paramIdx2Name)
-
-    logger.debug("Start training on multi-device")
-    Model.trainMultiDevice(
-      symbol, ctx, argNames, paramNames, auxNames,
-      _argParams, _auxParams,
-      this.beginEpoch, this.numEpoch,
-      this.epochSize, this.optimizer,
-      kvStore, updateOnKVStore,
-      trainData = trainData, evalData = Option(evalData),
-      evalMetric = evalMetric,
-      epochEndCallback = Option(epochEndCallback),
-      batchEndCallback = Option(batchEndCallback),
-      workLoadList = workLoadList,
-      monitor = monitor,
-      symGen = symGen)
+      val batchSize = trainData.batchSize * batchSizeMultiplier.getOrElse(1)
+      this.optimizer.setArgNames(argNames)
+      this.optimizer.setRescaleGrad(1f / batchSize)
+      this.optimizer.setSymbol(this.symbol)
+      val paramIdx2Name =
+        if (updateOnKVStore) {
+          paramNames.zipWithIndex.map { case (name, idx) => idx -> name }.toMap
+        } else {
+          paramNames.zipWithIndex.flatMap { case (name, idx) =>
+            (0 until ctx.length).map(k => (idx * ctx.length + k) -> name).toMap
+          }.toMap
+        }
+      this.optimizer.setIdx2Name(paramIdx2Name)
+
+      logger.debug("Start training on multi-device")
+      Model.trainMultiDevice(
+        symbol, ctx, argNames, paramNames, auxNames,
+        _argParams, _auxParams,
+        this.beginEpoch, this.numEpoch,
+        this.epochSize, this.optimizer,
+        kvStore, updateOnKVStore,
+        trainData = trainData, evalData = Option(evalData),
+        evalMetric = evalMetric,
+        epochEndCallback = Option(epochEndCallback),
+        batchEndCallback = Option(batchEndCallback),
+        workLoadList = workLoadList,
+        monitor = monitor,
+        symGen = symGen)
   }
 
   /**
@@ -416,9 +438,29 @@ class FeedForward private(
   def serialize(): Array[Byte] = {
     Model.serialize(this.symbol, getArgParams, getAuxParams)
   }
+
+  // hack to make the FeedForward.scala work with ResourceScope and
+  // automatically release _argParms and _auxParms
+  override def nativeAddress: CPtrAddress = hashCode()
+
+  override def nativeDeAllocator: CPtrAddress => Int = FeedForward.doNothingDeAllocator
+
+  override val ref: NativeResourceRef = super.register()
+
+  override val bytesAllocated: Long = 0L
+
+  override def dispose(): Unit = {
+    if (!super.isDisposed) {
+      _argParams.foreach { case (_, param) => param.dispose() }
+      _auxParams.foreach { case (_, param) => param.dispose() }
+    }
+  }
 }
 
 object FeedForward {
+
+  private def doNothingDeAllocator(dummy: CPtrAddress): Int = 0
+
   private val logger: Logger = LoggerFactory.getLogger(classOf[FeedForward])
   // Check if name is a data argument.
   private def isDataArg(name: String): Boolean = {
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/KVStore.scala b/scala-package/core/src/main/scala/org/apache/mxnet/KVStore.scala
index 8e89ce76b87..45189a13aef 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/KVStore.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/KVStore.scala
@@ -52,22 +52,17 @@ object KVStore {
   }
 }
 
-class KVStore(private[mxnet] val handle: KVStoreHandle) extends WarnIfNotDisposed {
+class KVStore(private[mxnet] val handle: KVStoreHandle) extends NativeResource {
   private val logger: Logger = LoggerFactory.getLogger(classOf[KVStore])
   private var updaterFunc: MXKVStoreUpdater = null
-  private var disposed = false
-  protected def isDisposed = disposed
 
-  /**
-   * Release the native memory.
-   * The object shall never be used after it is disposed.
-   */
-  def dispose(): Unit = {
-    if (!disposed) {
-      _LIB.mxKVStoreFree(handle)
-      disposed = true
-    }
-  }
+  override def nativeAddress: CPtrAddress = handle
+
+  override def nativeDeAllocator: CPtrAddress => MXUint = _LIB.mxKVStoreFree
+
+  override val ref: NativeResourceRef = super.register()
+
+  override val bytesAllocated: Long = 0L
 
   /**
    * Initialize a single or a sequence of key-value pairs into the store.
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/Model.scala b/scala-package/core/src/main/scala/org/apache/mxnet/Model.scala
index 4bb9cdd331a..b835c4964dd 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/Model.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/Model.scala
@@ -259,7 +259,9 @@ object Model {
                                       workLoadList: Seq[Float] = Nil,
                                       monitor: Option[Monitor] = None,
                                       symGen: SymbolGenerator = null): Unit = {
-    val executorManager = new DataParallelExecutorManager(
+    ResourceScope.using() {
+
+      val executorManager = new DataParallelExecutorManager(
         symbol = symbol,
         symGen = symGen,
         ctx = ctx,
@@ -269,17 +271,17 @@ object Model {
         auxNames = auxNames,
         workLoadList = workLoadList)
 
-    monitor.foreach(executorManager.installMonitor)
-    executorManager.setParams(argParams, auxParams)
+      monitor.foreach(executorManager.installMonitor)
+      executorManager.setParams(argParams, auxParams)
 
-    // updater for updateOnKVStore = false
-    val updaterLocal = Optimizer.getUpdater(optimizer)
+      // updater for updateOnKVStore = false
+      val updaterLocal = Optimizer.getUpdater(optimizer)
 
-    kvStore.foreach(initializeKVStore(_, executorManager.paramArrays,
-      argParams, executorManager.paramNames, updateOnKVStore))
-    if (updateOnKVStore) {
-      kvStore.foreach(_.setOptimizer(optimizer))
-    }
+      kvStore.foreach(initializeKVStore(_, executorManager.paramArrays,
+        argParams, executorManager.paramNames, updateOnKVStore))
+      if (updateOnKVStore) {
+        kvStore.foreach(_.setOptimizer(optimizer))
+      }
 
     // Now start training
     for (epoch <- beginEpoch until endEpoch) {
@@ -290,45 +292,46 @@ object Model {
       var epochDone = false
       // Iterate over training data.
       trainData.reset()
-      while (!epochDone) {
-        var doReset = true
-        while (doReset && trainData.hasNext) {
-          val dataBatch = trainData.next()
-          executorManager.loadDataBatch(dataBatch)
-          monitor.foreach(_.tic())
-          executorManager.forward(isTrain = true)
-          executorManager.backward()
-          if (updateOnKVStore) {
-            updateParamsOnKVStore(executorManager.paramArrays,
-              executorManager.gradArrays,
-              kvStore, executorManager.paramNames)
-          } else {
-            updateParams(executorManager.paramArrays,
-              executorManager.gradArrays,
-              updaterLocal, ctx.length,
-              executorManager.paramNames,
-              kvStore)
-          }
-          monitor.foreach(_.tocPrint())
-          // evaluate at end, so out_cpu_array can lazy copy
-          executorManager.updateMetric(evalMetric, dataBatch.label)
+      ResourceScope.using() {
+        while (!epochDone) {
+          var doReset = true
+          while (doReset && trainData.hasNext) {
+            val dataBatch = trainData.next()
+            executorManager.loadDataBatch(dataBatch)
+            monitor.foreach(_.tic())
+            executorManager.forward(isTrain = true)
+            executorManager.backward()
+            if (updateOnKVStore) {
+              updateParamsOnKVStore(executorManager.paramArrays,
+                executorManager.gradArrays,
+                kvStore, executorManager.paramNames)
+            } else {
+              updateParams(executorManager.paramArrays,
+                executorManager.gradArrays,
+                updaterLocal, ctx.length,
+                executorManager.paramNames,
+                kvStore)
+            }
+            monitor.foreach(_.tocPrint())
+            // evaluate at end, so out_cpu_array can lazy copy
+            executorManager.updateMetric(evalMetric, dataBatch.label)
 
-          nBatch += 1
-          batchEndCallback.foreach(_.invoke(epoch, nBatch, evalMetric))
+            nBatch += 1
+            batchEndCallback.foreach(_.invoke(epoch, nBatch, evalMetric))
 
-          // this epoch is done possibly earlier
-          if (epochSize != -1 && nBatch >= epochSize) {
-            doReset = false
+            // this epoch is done possibly earlier
+            if (epochSize != -1 && nBatch >= epochSize) {
+              doReset = false
+            }
+          }
+          if (doReset) {
+            trainData.reset()
           }
-        }
-        if (doReset) {
-          trainData.reset()
-        }
 
-        // this epoch is done
-        epochDone = (epochSize == -1 || nBatch >= epochSize)
+          // this epoch is done
+          epochDone = (epochSize == -1 || nBatch >= epochSize)
+        }
       }
-
       val (name, value) = evalMetric.get
       name.zip(value).foreach { case (n, v) =>
         logger.info(s"Epoch[$epoch] Train-$n=$v")
@@ -336,20 +339,22 @@ object Model {
       val toc = System.currentTimeMillis
       logger.info(s"Epoch[$epoch] Time cost=${toc - tic}")
 
-      evalData.foreach { evalDataIter =>
-        evalMetric.reset()
-        evalDataIter.reset()
-        // TODO: make DataIter implement Iterator
-        while (evalDataIter.hasNext) {
-          val evalBatch = evalDataIter.next()
-          executorManager.loadDataBatch(evalBatch)
-          executorManager.forward(isTrain = false)
-          executorManager.updateMetric(evalMetric, evalBatch.label)
-        }
+      ResourceScope.using() {
+        evalData.foreach { evalDataIter =>
+          evalMetric.reset()
+          evalDataIter.reset()
+          // TODO: make DataIter implement Iterator
+          while (evalDataIter.hasNext) {
+            val evalBatch = evalDataIter.next()
+            executorManager.loadDataBatch(evalBatch)
+            executorManager.forward(isTrain = false)
+            executorManager.updateMetric(evalMetric, evalBatch.label)
+          }
 
-        val (name, value) = evalMetric.get
-        name.zip(value).foreach { case (n, v) =>
-          logger.info(s"Epoch[$epoch] Train-$n=$v")
+          val (name, value) = evalMetric.get
+          name.zip(value).foreach { case (n, v) =>
+            logger.info(s"Epoch[$epoch] Validation-$n=$v")
+          }
         }
       }
 
@@ -359,8 +364,7 @@ object Model {
       epochEndCallback.foreach(_.invoke(epoch, symbol, argParams, auxParams))
     }
 
-    updaterLocal.dispose()
-    executorManager.dispose()
+    }
   }
   // scalastyle:on parameterNum
 }
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/NDArray.scala b/scala-package/core/src/main/scala/org/apache/mxnet/NDArray.scala
index 9b6a7dc6654..f2a7603caa8 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/NDArray.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/NDArray.scala
@@ -562,16 +562,20 @@ object NDArray extends NDArrayBase {
  */
 class NDArray private[mxnet](private[mxnet] val handle: NDArrayHandle,
                              val writable: Boolean = true,
-                             addToCollector: Boolean = true) extends WarnIfNotDisposed {
+                             addToCollector: Boolean = true) extends NativeResource {
   if (addToCollector) {
     NDArrayCollector.collect(this)
   }
 
+  override def nativeAddress: CPtrAddress = handle
+  override def nativeDeAllocator: (CPtrAddress => Int) = _LIB.mxNDArrayFree
+  override val bytesAllocated: Long = DType.numOfBytes(this.dtype) * this.shape.product
+
+  override val ref: NativeResourceRef = super.register()
+
   // record arrays who construct this array instance
   // we use weak reference to prevent gc blocking
   private[mxnet] val dependencies = mutable.HashMap.empty[Long, WeakReference[NDArray]]
-  @volatile private var disposed = false
-  def isDisposed: Boolean = disposed
 
   def serialize(): Array[Byte] = {
     val buf = ArrayBuffer.empty[Byte]
@@ -584,11 +588,10 @@ class NDArray private[mxnet](private[mxnet] val handle: NDArrayHandle,
    * The NDArrays it depends on will NOT be disposed. <br />
    * The object shall never be used after it is disposed.
    */
-  def dispose(): Unit = {
-    if (!disposed) {
-      _LIB.mxNDArrayFree(handle)
+  override def dispose(): Unit = {
+    if (!super.isDisposed) {
+      super.dispose()
       dependencies.clear()
-      disposed = true
     }
   }
 
@@ -1034,6 +1037,7 @@ class NDArray private[mxnet](private[mxnet] val handle: NDArrayHandle,
     // TODO: naive implementation
     shape.hashCode + toArray.hashCode
   }
+
 }
 
 private[mxnet] object NDArrayConversions {
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/NativeResource.scala b/scala-package/core/src/main/scala/org/apache/mxnet/NativeResource.scala
new file mode 100644
index 00000000000..1806b865337
--- /dev/null
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/NativeResource.scala
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet
+
+import org.apache.mxnet.Base.CPtrAddress
+import java.lang.ref.{PhantomReference, ReferenceQueue, WeakReference}
+import java.util.concurrent._
+
+import org.apache.mxnet.Base.checkCall
+import java.util.concurrent.atomic.AtomicLong
+
+
+/**
+  * NativeResource trait is used to manage MXNet Objects
+  * such as NDArray, Symbol, Executor, etc.,
+  * The MXNet Object calls NativeResource.register
+  * and assign the returned NativeResourceRef to PhantomReference
+  * NativeResource also implements AutoCloseable so MXNetObjects
+  * can be used like Resources in try-with-resources paradigm
+  */
+private[mxnet] trait NativeResource
+  extends AutoCloseable with WarnIfNotDisposed {
+
+  /**
+    * native Address associated with this object
+    */
+  def nativeAddress: CPtrAddress
+
+  /**
+    * Function Pointer to the NativeDeAllocator of nativeAddress
+    */
+  def nativeDeAllocator: (CPtrAddress => Int)
+
+  /**
+    * Call NativeResource.register to get the reference
+    */
+  val ref: NativeResourceRef
+
+  /**
+    * Off-Heap Bytes Allocated for this object
+    */
+  // intentionally making it a val, so it gets evaluated when defined
+  val bytesAllocated: Long
+
+  // this is set and unset by [[ResourceScope.add]] and [[ResourceScope.remove]]
+  private[mxnet] var scope: Option[ResourceScope] = None
+
+  @volatile private var disposed = false
+
+  override def isDisposed: Boolean = disposed || isDeAllocated
+
+  /**
+    * Register this object for PhantomReference tracking and in
+    * ResourceScope if used inside ResourceScope.
+    * @return NativeResourceRef that tracks reachability of this object
+    *         using PhantomReference
+    */
+  def register(): NativeResourceRef = {
+    val scope = ResourceScope.getCurrentScope()
+    if (scope.isDefined) scope.get.add(this)
+
+    NativeResource.totalBytesAllocated.getAndAdd(bytesAllocated)
+    // register with PhantomRef tracking to release in case the objects go
+    // out of reference within scope but are held for long time
+    NativeResourceRef.register(this, nativeDeAllocator)
+ }
+
+  // Implements [[@link AutoCloseable.close]]
+  override def close(): Unit = {
+    dispose()
+  }
+
+  // Implements [[@link WarnIfNotDisposed.dispose]]
+  def dispose(): Unit = dispose(true)
+
+  /**
+    * This method deAllocates nativeResource and deRegisters
+    * from PhantomRef and removes from Scope if
+    * removeFromScope is set to true.
+    * @param removeFromScope remove from the currentScope if true
+    */
+  // the parameter here controls whether to remove from current scope.
+  // [[ResourceScope.close]] calls NativeResource.dispose
+  // if we remove from the ResourceScope ie., from the container in ResourceScope.
+  // while iterating on the container, calling iterator.next is undefined and not safe.
+  // Note that ResourceScope automatically disposes all the resources within.
+  private[mxnet] def dispose(removeFromScope: Boolean = true): Unit = {
+    if (!disposed) {
+      checkCall(nativeDeAllocator(this.nativeAddress))
+      NativeResourceRef.deRegister(ref) // removes from PhantomRef tracking
+      if (removeFromScope && scope.isDefined) scope.get.remove(this)
+      NativeResource.totalBytesAllocated.getAndAdd(-1*bytesAllocated)
+      disposed = true
+    }
+  }
+
+  /*
+  this is used by the WarnIfNotDisposed finalizer,
+  the object could be disposed by the GC without the need for explicit disposal
+  but the finalizer might not have run, then the WarnIfNotDisposed throws a warning
+   */
+  private[mxnet] def isDeAllocated(): Boolean = NativeResourceRef.isDeAllocated(ref)
+
+}
+
+private[mxnet] object NativeResource {
+  var totalBytesAllocated : AtomicLong = new AtomicLong(0)
+}
+
+// Do not make [[NativeResource.resource]] a member of the class,
+// this will hold reference and GC will not clear the object.
+private[mxnet] class NativeResourceRef(resource: NativeResource,
+                                       val resourceDeAllocator: CPtrAddress => Int)
+        extends PhantomReference[NativeResource](resource, NativeResourceRef.refQ) {}
+
+private[mxnet] object NativeResourceRef {
+
+  private[mxnet] val refQ: ReferenceQueue[NativeResource]
+                = new ReferenceQueue[NativeResource]
+
+  private[mxnet] val refMap = new ConcurrentHashMap[NativeResourceRef, CPtrAddress]()
+
+  private[mxnet] val cleaner = new ResourceCleanupThread()
+
+  cleaner.start()
+
+  def register(resource: NativeResource, nativeDeAllocator: (CPtrAddress => Int)):
+  NativeResourceRef = {
+    val ref = new NativeResourceRef(resource, nativeDeAllocator)
+    refMap.put(ref, resource.nativeAddress)
+    ref
+  }
+
+  // remove from PhantomRef tracking
+  def deRegister(ref: NativeResourceRef): Unit = refMap.remove(ref)
+
+  /**
+    * This method will check if the cleaner ran and deAllocated the object
+    * As a part of GC, when the object is unreachable GC inserts a phantomRef
+    * to the ReferenceQueue which the cleaner thread will deallocate, however
+    * the finalizer runs much later depending on the GC.
+    * @param resource resource to verify if it has been deAllocated
+    * @return true if already deAllocated
+    */
+  def isDeAllocated(ref: NativeResourceRef): Boolean = {
+    !refMap.containsKey(ref)
+  }
+
+  def cleanup: Unit = {
+    // remove is a blocking call
+    val ref: NativeResourceRef = refQ.remove().asInstanceOf[NativeResourceRef]
+    // phantomRef will be removed from the map when NativeResource.close is called.
+    val resource = refMap.get(ref)
+    if (resource != 0L)  { // since CPtrAddress is Scala a Long, it cannot be null
+      ref.resourceDeAllocator(resource)
+      refMap.remove(ref)
+    }
+  }
+
+  protected class ResourceCleanupThread extends Thread {
+    setPriority(Thread.MAX_PRIORITY)
+    setName("NativeResourceDeAllocatorThread")
+    setDaemon(true)
+
+    override def run(): Unit = {
+      while (true) {
+        try {
+          NativeResourceRef.cleanup
+        }
+        catch {
+          case _: InterruptedException => Thread.currentThread().interrupt()
+        }
+      }
+    }
+  }
+}
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/Optimizer.scala b/scala-package/core/src/main/scala/org/apache/mxnet/Optimizer.scala
index 758cbc82961..c3f8aaec6d6 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/Optimizer.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/Optimizer.scala
@@ -19,6 +19,8 @@ package org.apache.mxnet
 
 import java.io._
 
+import org.apache.mxnet.Base.CPtrAddress
+
 import scala.collection.mutable
 import scala.util.Either
 
@@ -38,8 +40,10 @@ object Optimizer {
       }
 
       override def dispose(): Unit = {
-        states.values.foreach(optimizer.disposeState)
-        states.clear()
+        if (!super.isDisposed) {
+          states.values.foreach(optimizer.disposeState)
+          states.clear()
+        }
       }
 
       override def serializeState(): Array[Byte] = {
@@ -285,7 +289,8 @@ abstract class Optimizer extends Serializable {
   }
 }
 
-trait MXKVStoreUpdater {
+trait MXKVStoreUpdater extends
+  NativeResource {
   /**
    * user-defined updater for the kvstore
    * It's this updater's responsibility to delete recv and local
@@ -294,9 +299,14 @@ trait MXKVStoreUpdater {
    * @param local the value stored on local on this key
    */
   def update(key: Int, recv: NDArray, local: NDArray): Unit
-  def dispose(): Unit
-  // def serializeState(): Array[Byte]
-  // def deserializeState(bytes: Array[Byte]): Unit
+
+  // This is a hack to make Optimizers work with ResourceScope
+  // otherwise the user has to manage calling dispose on this object.
+  override def nativeAddress: CPtrAddress = hashCode()
+  override def nativeDeAllocator: CPtrAddress => Int = doNothingDeAllocator
+  private def doNothingDeAllocator(dummy: CPtrAddress): Int = 0
+  override val ref: NativeResourceRef = super.register()
+  override val bytesAllocated: Long = 0L
 }
 
 trait MXKVStoreCachedStates {
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/ResourceScope.scala b/scala-package/core/src/main/scala/org/apache/mxnet/ResourceScope.scala
new file mode 100644
index 00000000000..30fe1473a2c
--- /dev/null
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/ResourceScope.scala
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet
+
+import java.util.HashSet
+
+import org.slf4j.LoggerFactory
+
+import scala.collection.mutable
+import scala.collection.mutable.ArrayBuffer
+import scala.util.Try
+import scala.util.control.{ControlThrowable, NonFatal}
+
+/**
+  * This class manages automatically releasing of [[NativeResource]]s
+  */
+class ResourceScope extends AutoCloseable {
+
+  // HashSet does not take a custom comparator
+  private[mxnet] val resourceQ = new mutable.TreeSet[NativeResource]()(nativeAddressOrdering)
+
+  private object nativeAddressOrdering extends Ordering[NativeResource] {
+    def compare(a: NativeResource, b: NativeResource): Int = {
+      a.nativeAddress compare  b.nativeAddress
+    }
+  }
+
+  ResourceScope.addToThreadLocal(this)
+
+  /**
+    * Releases all the [[NativeResource]] by calling
+    * the associated [[NativeResource.close()]] method
+    */
+  override def close(): Unit = {
+    ResourceScope.removeFromThreadLocal(this)
+    resourceQ.foreach(resource => if (resource != null) resource.dispose(false) )
+    resourceQ.clear()
+  }
+
+  /**
+    * Add a NativeResource to the scope
+    * @param resource
+    */
+  def add(resource: NativeResource): Unit = {
+    resourceQ.+=(resource)
+    resource.scope = Some(this)
+  }
+
+  /**
+    * Remove NativeResource from the Scope, this uses
+    * object equality to find the resource in the stack.
+    * @param resource
+    */
+  def remove(resource: NativeResource): Unit = {
+    resourceQ.-=(resource)
+    resource.scope = None
+  }
+
+  /**
+    * Removes from current Scope and moves to outer scope if it exists
+    * @param resource Resource to be moved to an outer scope
+    */
+  def moveToOuterScope(resource: NativeResource): Unit = {
+    val prevScope: Option[ResourceScope] = ResourceScope.getPrevScope()
+    if (prevScope.isDefined) {
+      this.remove(resource)
+      prevScope.get.add(resource)
+    } else this.remove(resource)
+  }
+
+}
+
+object ResourceScope {
+
+  private val logger = LoggerFactory.getLogger(classOf[ResourceScope])
+
+  /**
+    * Captures all Native Resources created using the ResourceScope and
+    * at the end of the body, de allocates all the Native resources by calling close on them.
+    * This method will not deAllocate NativeResources returned from the block.
+    * @param scope (Optional). Scope in which to capture the native resources
+    * @param body  block of code to execute in this scope
+    * @tparam A return type
+    * @return result of the operation, if the result is of type NativeResource, it is not
+    *         de allocated so the user can use it and then de allocate manually by calling
+    *         close or enclose in another resourceScope.
+    */
+  // inspired from slide 21 of https://www.slideshare.net/Odersky/fosdem-2009-1013261
+  // and https://github.com/scala/scala/blob/2.13.x/src/library/scala/util/Using.scala
+  // TODO: we should move to the Scala util's Using method when we move to Scala 2.13
+  def using[A](scope: ResourceScope = null)(body: => A): A = {
+
+    val curScope = if (scope != null) scope else new ResourceScope()
+
+    @inline def resourceInGeneric(g: scala.collection.Iterable[_]) = {
+      g.foreach( n =>
+        n match {
+          case nRes: NativeResource => {
+            curScope.moveToOuterScope(nRes)
+          }
+          case kv: scala.Tuple2[_, _] => {
+            if (kv._1.isInstanceOf[NativeResource]) curScope.moveToOuterScope(
+              kv._1.asInstanceOf[NativeResource])
+            if (kv._2.isInstanceOf[NativeResource]) curScope.moveToOuterScope(
+              kv._2.asInstanceOf[NativeResource])
+          }
+        }
+      )
+    }
+
+    @inline def safeAddSuppressed(t: Throwable, suppressed: Throwable): Unit = {
+      if (!t.isInstanceOf[ControlThrowable]) t.addSuppressed(suppressed)
+    }
+
+    var retThrowable: Throwable = null
+
+    try {
+      val ret = body
+       ret match {
+          // don't de-allocate if returning any collection that contains NativeResource.
+        case resInGeneric: scala.collection.Iterable[_] => resourceInGeneric(resInGeneric)
+        case nRes: NativeResource => curScope.moveToOuterScope(nRes)
+        case ndRet: NDArrayFuncReturn => ndRet.arr.foreach( nd => curScope.moveToOuterScope(nd) )
+        case _ => // do nothing
+      }
+      ret
+    } catch {
+      case t: Throwable =>
+        retThrowable = t
+        null.asInstanceOf[A] // we'll throw in finally
+    } finally {
+      var toThrow: Throwable = retThrowable
+      if (retThrowable eq null) curScope.close()
+      else {
+        try {
+          curScope.close
+        } catch {
+          case closeThrowable: Throwable =>
+            if (NonFatal(retThrowable) && !NonFatal(closeThrowable)) toThrow = closeThrowable
+            else safeAddSuppressed(retThrowable, closeThrowable)
+        } finally {
+          throw toThrow
+        }
+      }
+    }
+  }
+
+  // thread local Scopes
+  private[mxnet] val threadLocalScopes = new ThreadLocal[ArrayBuffer[ResourceScope]] {
+    override def initialValue(): ArrayBuffer[ResourceScope] =
+      new ArrayBuffer[ResourceScope]()
+  }
+
+  /**
+    * Add resource to current ThreadLocal DataStructure
+    * @param r ResourceScope to add.
+    */
+  private[mxnet] def addToThreadLocal(r: ResourceScope): Unit = {
+    threadLocalScopes.get() += r
+  }
+
+  /**
+    * Remove resource from current ThreadLocal DataStructure
+    * @param r ResourceScope to remove
+    */
+  private[mxnet] def removeFromThreadLocal(r: ResourceScope): Unit = {
+    threadLocalScopes.get() -= r
+  }
+
+  /**
+    * Get the latest Scope in the stack
+    * @return
+    */
+  private[mxnet] def getCurrentScope(): Option[ResourceScope] = {
+    Try(Some(threadLocalScopes.get().last)).getOrElse(None)
+  }
+
+  /**
+    * Get the Last but one Scope from threadLocal Scopes.
+    * @return n-1th scope or None when not found
+    */
+  private[mxnet] def getPrevScope(): Option[ResourceScope] = {
+    val scopes = threadLocalScopes.get()
+    Try(Some(scopes(scopes.size - 2))).getOrElse(None)
+  }
+}
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/Symbol.scala b/scala-package/core/src/main/scala/org/apache/mxnet/Symbol.scala
index b1a3e392f41..a009e7e343f 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/Symbol.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/Symbol.scala
@@ -29,21 +29,15 @@ import scala.collection.mutable.{ArrayBuffer, ListBuffer}
  * WARNING: it is your responsibility to clear this object through dispose().
  * </b>
  */
-class Symbol private(private[mxnet] val handle: SymbolHandle) extends WarnIfNotDisposed {
+class Symbol private(private[mxnet] val handle: SymbolHandle) extends NativeResource {
   private val logger: Logger = LoggerFactory.getLogger(classOf[Symbol])
-  private var disposed = false
-  protected def isDisposed = disposed
 
-  /**
-   * Release the native memory.
-   * The object shall never be used after it is disposed.
-   */
-  def dispose(): Unit = {
-    if (!disposed) {
-      _LIB.mxSymbolFree(handle)
-      disposed = true
-    }
-  }
+  // unable to get the byteAllocated for Symbol
+  override val bytesAllocated: Long = 0L
+  override def nativeAddress: CPtrAddress = handle
+  override def nativeDeAllocator: (CPtrAddress => Int) = _LIB.mxSymbolFree
+  override val ref: NativeResourceRef = super.register()
+
 
   def +(other: Symbol): Symbol = Symbol.createFromListedSymbols("_Plus")(Array(this, other))
   def +[@specialized(Int, Float, Double) V](other: V): Symbol = {
@@ -793,7 +787,7 @@ class Symbol private(private[mxnet] val handle: SymbolHandle) extends WarnIfNotD
     }
 
     val execHandle = new ExecutorHandleRef
-    val sharedHadle = if (sharedExec != null) sharedExec.handle else 0L
+    val sharedHandle = if (sharedExec != null) sharedExec.handle else 0L
     checkCall(_LIB.mxExecutorBindEX(handle,
                                    ctx.deviceTypeid,
                                    ctx.deviceId,
@@ -806,7 +800,7 @@ class Symbol private(private[mxnet] val handle: SymbolHandle) extends WarnIfNotD
                                    argsGradHandle,
                                    reqsArray,
                                    auxArgsHandle,
-                                   sharedHadle,
+                                   sharedHandle,
                                    execHandle))
     val executor = new Executor(execHandle.value, this.clone())
     executor.argArrays = argsNDArray
@@ -832,6 +826,7 @@ class Symbol private(private[mxnet] val handle: SymbolHandle) extends WarnIfNotD
     checkCall(_LIB.mxSymbolSaveToJSON(handle, jsonStr))
     jsonStr.value
   }
+
 }
 
 /**
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/io/MXDataIter.scala b/scala-package/core/src/main/scala/org/apache/mxnet/io/MXDataIter.scala
index f7f858deb82..998017750db 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/io/MXDataIter.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/io/MXDataIter.scala
@@ -33,7 +33,7 @@ import scala.collection.mutable.ListBuffer
 private[mxnet] class MXDataIter(private[mxnet] val handle: DataIterHandle,
                                 dataName: String = "data",
                                 labelName: String = "label")
-  extends DataIter with WarnIfNotDisposed {
+  extends DataIter with NativeResource {
 
   private val logger = LoggerFactory.getLogger(classOf[MXDataIter])
 
@@ -67,20 +67,13 @@ private[mxnet] class MXDataIter(private[mxnet] val handle: DataIterHandle,
     }
   }
 
+  override def nativeAddress: CPtrAddress = handle
 
-  private var disposed = false
-  protected def isDisposed = disposed
+  override def nativeDeAllocator: CPtrAddress => MXUint = _LIB.mxDataIterFree
 
-  /**
-   * Release the native memory.
-   * The object shall never be used after it is disposed.
-   */
-  def dispose(): Unit = {
-    if (!disposed) {
-      _LIB.mxDataIterFree(handle)
-      disposed = true
-    }
-  }
+  override val ref: NativeResourceRef = super.register()
+
+  override val bytesAllocated: Long = 0L
 
   /**
    * reset the iterator
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/Context.scala b/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/Context.scala
index 5f0caedcc40..ac3517b151f 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/Context.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/Context.scala
@@ -18,6 +18,13 @@ package org.apache.mxnet.javaapi
 
 import collection.JavaConverters._
 
+/**
+  * Constructing a context which is used to specify the device and device type that will
+  * be utilized by the engine.
+  *
+  * @param deviceTypeName {'cpu', 'gpu'} String representing the device type
+  * @param deviceId The device id of the device, needed for GPU
+  */
 class Context(val context: org.apache.mxnet.Context) {
 
   val deviceTypeid: Int = context.deviceTypeid
@@ -26,6 +33,11 @@ class Context(val context: org.apache.mxnet.Context) {
   = this(new org.apache.mxnet.Context(deviceTypeName, deviceId))
 
   def withScope[T](body: => T): T = context.withScope(body)
+
+  /**
+    * Return device type of current context.
+    * @return device_type
+    */
   def deviceType: String = context.deviceType
 
   override def toString: String = context.toString
@@ -42,6 +54,5 @@ object Context {
   val gpu: Context = org.apache.mxnet.Context.gpu()
   val devtype2str = org.apache.mxnet.Context.devstr2type.asJava
   val devstr2type = org.apache.mxnet.Context.devstr2type.asJava
-
   def defaultCtx: Context = org.apache.mxnet.Context.defaultCtx
 }
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/IO.scala b/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/IO.scala
index 47b1c367c1c..bf961b2bd52 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/IO.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/IO.scala
@@ -30,5 +30,13 @@ object DataDesc{
 
   implicit def toDataDesc(dataDesc: DataDesc): org.apache.mxnet.DataDesc = dataDesc.dataDesc
 
+  /**
+    * Get the dimension that corresponds to the batch size.
+    * @param layout layout string. For example, "NCHW".
+    * @return An axis indicating the batch_size dimension. When data-parallelism is used,
+    *         the data will be automatically split and concatenate along the batch_size dimension.
+    *         Axis can be -1, which means the whole array will be copied
+    *         for each data-parallelism device.
+    */
   def getBatchAxis(layout: String): Int = org.apache.mxnet.DataDesc.getBatchAxis(Some(layout))
 }
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/NDArray.scala b/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/NDArray.scala
new file mode 100644
index 00000000000..cdcc292ada6
--- /dev/null
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/javaapi/NDArray.scala
@@ -0,0 +1,387 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet.javaapi
+
+import org.apache.mxnet.javaapi.DType.DType
+
+import collection.JavaConverters._
+
+@AddJNDArrayAPIs(false)
+object NDArray extends NDArrayBase {
+  implicit def fromNDArray(nd: org.apache.mxnet.NDArray): NDArray = new NDArray(nd)
+
+  implicit def toNDArray(jnd: NDArray): org.apache.mxnet.NDArray = jnd.nd
+
+  def waitall(): Unit = org.apache.mxnet.NDArray.waitall()
+
+  /**
+    * One hot encoding indices into matrix out.
+    * @param indices An NDArray containing indices of the categorical features.
+    * @param out The result holder of the encoding.
+    * @return Same as out.
+    */
+  def onehotEncode(indices: NDArray, out: NDArray): NDArray
+  = org.apache.mxnet.NDArray.onehotEncode(indices, out)
+
+  /**
+    * Create an empty uninitialized new NDArray, with specified shape.
+    *
+    * @param shape shape of the NDArray.
+    * @param ctx The context of the NDArray.
+    *
+    * @return The created NDArray.
+    */
+  def empty(shape: Shape, ctx: Context, dtype: DType.DType): NDArray
+  = org.apache.mxnet.NDArray.empty(shape, ctx, dtype)
+  def empty(ctx: Context, shape: Array[Int]): NDArray
+  = org.apache.mxnet.NDArray.empty(new Shape(shape), ctx)
+  def empty(ctx : Context, shape : java.util.List[java.lang.Integer]) : NDArray
+  = org.apache.mxnet.NDArray.empty(new Shape(shape), ctx)
+
+  /**
+    * Create a new NDArray filled with 0, with specified shape.
+    *
+    * @param shape shape of the NDArray.
+    * @param ctx The context of the NDArray.
+    *
+    * @return The created NDArray.
+    */
+  def zeros(shape: Shape, ctx: Context, dtype: DType.DType): NDArray
+  = org.apache.mxnet.NDArray.zeros(shape, ctx, dtype)
+  def zeros(ctx: Context, shape: Array[Int]): NDArray
+  = org.apache.mxnet.NDArray.zeros(new Shape(shape), ctx)
+  def zeros(ctx : Context, shape : java.util.List[java.lang.Integer]) : NDArray
+  = org.apache.mxnet.NDArray.zeros(new Shape(shape), ctx)
+
+  /**
+    * Create a new NDArray filled with 1, with specified shape.
+    * @param shape shape of the NDArray.
+    * @param ctx The context of the NDArray.
+    * @return The created NDArray.
+    */
+  def ones(shape: Shape, ctx: Context, dtype: DType.DType): NDArray
+  = org.apache.mxnet.NDArray.ones(shape, ctx, dtype)
+  def ones(ctx: Context, shape: Array[Int]): NDArray
+  = org.apache.mxnet.NDArray.ones(new Shape(shape), ctx)
+  def ones(ctx : Context, shape : java.util.List[java.lang.Integer]) : NDArray
+  = org.apache.mxnet.NDArray.ones(new Shape(shape), ctx)
+
+  /**
+    * Create a new NDArray filled with given value, with specified shape.
+    * @param shape shape of the NDArray.
+    * @param value value to be filled with
+    * @param ctx The context of the NDArray
+    */
+  def full(shape: Shape, value: Float, ctx: Context): NDArray
+  = org.apache.mxnet.NDArray.full(shape, value, ctx)
+
+  def power(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.power(lhs, rhs)
+  def power(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.power(lhs, rhs)
+  def power(lhs: Float, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.power(lhs, rhs)
+
+  def maximum(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.maximum(lhs, rhs)
+  def maximum(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.maximum(lhs, rhs)
+  def maximum(lhs: Float, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.maximum(lhs, rhs)
+
+  def minimum(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.minimum(lhs, rhs)
+  def minimum(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.minimum(lhs, rhs)
+  def minimum(lhs: Float, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.minimum(lhs, rhs)
+
+
+  /**
+    * Returns the result of element-wise **equal to** (==) comparison operation with broadcasting.
+    * For each element in input arrays, return 1(true) if corresponding elements are same,
+    * otherwise return 0(false).
+    */
+  def equal(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.equal(lhs, rhs)
+  def equal(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.equal(lhs, rhs)
+
+  /**
+    * Returns the result of element-wise **not equal to** (!=) comparison operation
+    * with broadcasting.
+    * For each element in input arrays, return 1(true) if corresponding elements are different,
+    * otherwise return 0(false).
+    */
+  def notEqual(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.notEqual(lhs, rhs)
+  def notEqual(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.notEqual(lhs, rhs)
+
+  /**
+    * Returns the result of element-wise **greater than** (>) comparison operation
+    * with broadcasting.
+    * For each element in input arrays, return 1(true) if lhs elements are greater than rhs,
+    * otherwise return 0(false).
+    */
+  def greater(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.greater(lhs, rhs)
+  def greater(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.greater(lhs, rhs)
+
+  /**
+    * Returns the result of element-wise **greater than or equal to** (>=) comparison
+    * operation with broadcasting.
+    * For each element in input arrays, return 1(true) if lhs elements are greater than equal to rhs
+    * otherwise return 0(false).
+    */
+  def greaterEqual(lhs: NDArray, rhs: NDArray): NDArray
+  = org.apache.mxnet.NDArray.greaterEqual(lhs, rhs)
+  def greaterEqual(lhs: NDArray, rhs: Float): NDArray
+  = org.apache.mxnet.NDArray.greaterEqual(lhs, rhs)
+
+  /**
+    * Returns the result of element-wise **lesser than** (<) comparison operation
+    * with broadcasting.
+    * For each element in input arrays, return 1(true) if lhs elements are less than rhs,
+    * otherwise return 0(false).
+    */
+  def lesser(lhs: NDArray, rhs: NDArray): NDArray = org.apache.mxnet.NDArray.lesser(lhs, rhs)
+  def lesser(lhs: NDArray, rhs: Float): NDArray = org.apache.mxnet.NDArray.lesser(lhs, rhs)
+
+  /**
+    * Returns the result of element-wise **lesser than or equal to** (<=) comparison
+    * operation with broadcasting.
+    * For each element in input arrays, return 1(true) if lhs elements are
+    * lesser than equal to rhs, otherwise return 0(false).
+    */
+  def lesserEqual(lhs: NDArray, rhs: NDArray): NDArray
+  = org.apache.mxnet.NDArray.lesserEqual(lhs, rhs)
+  def lesserEqual(lhs: NDArray, rhs: Float): NDArray
+  = org.apache.mxnet.NDArray.lesserEqual(lhs, rhs)
+
+  /**
+    * Create a new NDArray that copies content from source_array.
+    * @param sourceArr Source data to create NDArray from.
+    * @param shape shape of the NDArray
+    * @param ctx The context of the NDArray, default to current default context.
+    * @return The created NDArray.
+    */
+  def array(sourceArr: java.util.List[java.lang.Float], shape: Shape, ctx: Context = null): NDArray
+  = org.apache.mxnet.NDArray.array(
+    sourceArr.asScala.map(ele => Float.unbox(ele)).toArray, shape, ctx)
+
+  /**
+    * Returns evenly spaced values within a given interval.
+    * Values are generated within the half-open interval [`start`, `stop`). In other
+    * words, the interval includes `start` but excludes `stop`.
+    * @param start Start of interval.
+    * @param stop End of interval.
+    * @param step Spacing between values.
+    * @param repeat Number of times to repeat each element.
+    * @param ctx Device context.
+    * @param dType The data type of the `NDArray`.
+    * @return NDArray of evenly spaced values in the specified range.
+    */
+  def arange(start: Float, stop: Float, step: Float, repeat: Int,
+             ctx: Context, dType: DType.DType): NDArray =
+    org.apache.mxnet.NDArray.arange(start, Some(stop), step, repeat, ctx, dType)
+}
+
+/**
+  * NDArray object in mxnet.
+  * NDArray is basic ndarray/Tensor like data structure in mxnet. <br />
+  * <b>
+  * NOTE: NDArray is stored in native memory. Use NDArray in a try-with-resources() construct
+  * or a [[ResourceScope]] in a try-with-resource to have them automatically disposed. You can
+  * explicitly control the lifetime of NDArray by calling dispose manually. Failure to do this
+  * will result in leaking native memory.
+  * </b>
+  */
+class NDArray(val nd : org.apache.mxnet.NDArray ) {
+
+  def this(arr : Array[Float], shape : Shape, ctx : Context) = {
+    this(org.apache.mxnet.NDArray.array(arr, shape, ctx))
+  }
+
+  def this(arr : java.util.List[java.lang.Float], shape : Shape, ctx : Context) = {
+    this(NDArray.array(arr, shape, ctx))
+  }
+
+  def serialize() : Array[Byte] = nd.serialize()
+
+  /**
+    * Release the native memory. <br />
+    * The NDArrays it depends on will NOT be disposed. <br />
+    * The object shall never be used after it is disposed.
+    */
+  def dispose() : Unit = nd.dispose()
+
+  /**
+    * Dispose all NDArrays who help to construct this array. <br />
+    * e.g. (a * b + c).disposeDeps() will dispose a, b, c (including their deps) and a * b
+    * @return this array
+    */
+  def disposeDeps() : NDArray = nd.disposeDepsExcept()
+  // def disposeDepsExcept(arr : Array[NDArray]) : NDArray = nd.disposeDepsExcept()
+
+  /**
+    * Return a sliced NDArray that shares memory with current one.
+    * NDArray only support continuous slicing on axis 0
+    *
+    * @param start Starting index of slice.
+    * @param stop Finishing index of slice.
+    *
+    * @return a sliced NDArray that shares memory with current one.
+    */
+  def slice(start : Int, stop : Int) : NDArray = nd.slice(start, stop)
+
+  /**
+    * Return a sliced NDArray at the ith position of axis0
+    * @param i
+    * @return a sliced NDArray that shares memory with current one.
+    */
+  def slice (i : Int) : NDArray = nd.slice(i)
+
+  /**
+    * Return a sub NDArray that shares memory with current one.
+    * the first axis will be rolled up, which causes its shape different from slice(i, i+1)
+    * @param idx index of sub array.
+    */
+  def at(idx : Int) : NDArray = nd.at(idx)
+
+  def T : NDArray = nd.T
+
+  /**
+    * Get data type of current NDArray.
+    * @return class representing type of current ndarray
+    */
+  def dtype : DType = nd.dtype
+
+  /**
+    * Return a copied numpy array of current array with specified type.
+    * @param dtype Desired type of result array.
+    * @return A copy of array content.
+    */
+  def asType(dtype : DType) : NDArray = nd.asType(dtype)
+
+  /**
+    * Return a reshaped NDArray that shares memory with current one.
+    * @param dims New shape.
+    *
+    * @return a reshaped NDArray that shares memory with current one.
+    */
+  def reshape(dims : Array[Int]) : NDArray = nd.reshape(dims)
+
+  /**
+    * Block until all pending writes operations on current NDArray are finished.
+    * This function will return when all the pending writes to the current
+    * NDArray finishes. There can still be pending read going on when the
+    * function returns.
+    */
+  def waitToRead(): Unit = nd.waitToRead()
+
+  /**
+    * Get context of current NDArray.
+    * @return The context of current NDArray.
+    */
+  def context : Context = nd.context
+
+  /**
+    * Set the values of the NDArray
+    * @param value Value to set
+    * @return Current NDArray
+    */
+  def set(value : Float) : NDArray = nd.set(value)
+  def set(other : NDArray) : NDArray = nd.set(other)
+  def set(other : Array[Float]) : NDArray = nd.set(other)
+
+  def add(other : NDArray) : NDArray = this.nd + other.nd
+  def add(other : Float) : NDArray = this.nd + other
+  def _add(other : NDArray) : NDArray = this.nd += other
+  def _add(other : Float) : NDArray = this.nd += other
+  def subtract(other : NDArray) : NDArray = this.nd - other
+  def subtract(other : Float) : NDArray = this.nd - other
+  def _subtract(other : NDArray) : NDArray = this.nd -= other
+  def _subtract(other : Float) : NDArray = this.nd -= other
+  def multiply(other : NDArray) : NDArray = this.nd * other
+  def multiply(other : Float) : NDArray = this.nd * other
+  def _multiply(other : NDArray) : NDArray = this.nd *= other
+  def _multiply(other : Float) : NDArray = this.nd *= other
+  def div(other : NDArray) : NDArray = this.nd / other
+  def div(other : Float) : NDArray = this.nd / other
+  def _div(other : NDArray) : NDArray = this.nd /= other
+  def _div(other : Float) : NDArray = this.nd /= other
+  def pow(other : NDArray) : NDArray = this.nd ** other
+  def pow(other : Float) : NDArray = this.nd ** other
+  def _pow(other : NDArray) : NDArray = this.nd **= other
+  def _pow(other : Float) : NDArray = this.nd **= other
+  def mod(other : NDArray) : NDArray = this.nd % other
+  def mod(other : Float) : NDArray = this.nd % other
+  def _mod(other : NDArray) : NDArray = this.nd %= other
+  def _mod(other : Float) : NDArray = this.nd %= other
+  def greater(other : NDArray) : NDArray = this.nd > other
+  def greater(other : Float) : NDArray = this.nd > other
+  def greaterEqual(other : NDArray) : NDArray = this.nd >= other
+  def greaterEqual(other : Float) : NDArray = this.nd >= other
+  def lesser(other : NDArray) : NDArray = this.nd < other
+  def lesser(other : Float) : NDArray = this.nd < other
+  def lesserEqual(other : NDArray) : NDArray = this.nd <= other
+  def lesserEqual(other : Float) : NDArray = this.nd <= other
+
+  /**
+    * Return a copied flat java array of current array (row-major).
+    * @return  A copy of array content.
+    */
+  def toArray : Array[Float] = nd.toArray
+
+  /**
+    * Return a CPU scalar(float) of current ndarray.
+    * This ndarray must have shape (1,)
+    *
+    * @return The scalar representation of the ndarray.
+    */
+  def toScalar : Float = nd.toScalar
+
+  /**
+    * Copy the content of current array to other.
+    *
+    * @param other Target NDArray or context we want to copy data to.
+    * @return The copy target NDArray
+    */
+  def copyTo(other : NDArray) : NDArray = nd.copyTo(other)
+
+  /**
+    * Copy the content of current array to a new NDArray in the context.
+    *
+    * @param ctx Target context we want to copy data to.
+    * @return The copy target NDArray
+    */
+  def copyTo(ctx : Context) : NDArray = nd.copyTo(ctx)
+
+  /**
+    * Clone the current array
+    * @return the copied NDArray in the same context
+    */
+  def copy() : NDArray = copyTo(this.context)
+
+  /**
+    * Get shape of current NDArray.
+    * @return an array representing shape of current ndarray
+    */
+  def shape : Shape = nd.shape
+
+
+  def size : Int = shape.product
+
+  /**
+    * Return an `NDArray` that lives in the target context. If the array
+    * is already in that context, `self` is returned. Otherwise, a copy is made.
+    * @param context The target context we want the return value to live in.
+    * @return A copy or `self` as an `NDArray` that lives in the target context.
+    */
+  def asInContext(context: Context): NDArray = nd.asInContext(context)
+
+  override def equals(obj: Any): Boolean = nd.equals(obj)
+  override def hashCode(): Int = nd.hashCode
+}
diff --git a/scala-package/core/src/main/scala/org/apache/mxnet/optimizer/SGD.scala b/scala-package/core/src/main/scala/org/apache/mxnet/optimizer/SGD.scala
index e20b433ed1e..d349feac3e9 100644
--- a/scala-package/core/src/main/scala/org/apache/mxnet/optimizer/SGD.scala
+++ b/scala-package/core/src/main/scala/org/apache/mxnet/optimizer/SGD.scala
@@ -17,7 +17,7 @@
 
 package org.apache.mxnet.optimizer
 
-import org.apache.mxnet.{Optimizer, LRScheduler, NDArray}
+import org.apache.mxnet._
 import org.apache.mxnet.NDArrayConversions._
 
 /**
@@ -92,7 +92,13 @@ class SGD(val learningRate: Float = 0.01f, momentum: Float = 0.0f,
     if (momentum == 0.0f) {
       null
     } else {
-      NDArray.zeros(weight.shape, weight.context)
+      val s = NDArray.zeros(weight.shape, weight.context)
+      // this is created on the fly and shared between runs,
+      // we don't want it to be dispose from the scope
+      // and should be handled by the dispose
+      val scope = ResourceScope.getCurrentScope()
+      if (scope.isDefined) scope.get.remove(s)
+      s
     }
   }
 
diff --git a/scala-package/core/src/test/java/org/apache/mxnet/javaapi/NDArrayTest.java b/scala-package/core/src/test/java/org/apache/mxnet/javaapi/NDArrayTest.java
new file mode 100644
index 00000000000..2659b7848bc
--- /dev/null
+++ b/scala-package/core/src/test/java/org/apache/mxnet/javaapi/NDArrayTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet.javaapi;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.mxnet.javaapi.NDArrayBase.*;
+
+import static org.junit.Assert.assertTrue;
+
+public class NDArrayTest {
+    @Test
+    public void testCreateNDArray() {
+        NDArray nd = new NDArray(new float[]{1.0f, 2.0f, 3.0f},
+                new Shape(new int[]{1, 3}),
+                new Context("cpu", 0));
+        int[] arr = new int[]{1, 3};
+        assertTrue(Arrays.equals(nd.shape().toArray(), arr));
+        assertTrue(nd.at(0).at(0).toArray()[0] == 1.0f);
+        List<Float> list = Arrays.asList(1.0f, 2.0f, 3.0f);
+        // Second way creating NDArray
+        nd = NDArray.array(list,
+                new Shape(new int[]{1, 3}),
+                new Context("cpu", 0));
+        assertTrue(Arrays.equals(nd.shape().toArray(), arr));
+    }
+
+    @Test
+    public void testZeroOneEmpty(){
+        NDArray ones = NDArray.ones(new Context("cpu", 0), new int[]{100, 100});
+        NDArray zeros = NDArray.zeros(new Context("cpu", 0), new int[]{100, 100});
+        NDArray empty = NDArray.empty(new Context("cpu", 0), new int[]{100, 100});
+        int[] arr = new int[]{100, 100};
+        assertTrue(Arrays.equals(ones.shape().toArray(), arr));
+        assertTrue(Arrays.equals(zeros.shape().toArray(), arr));
+        assertTrue(Arrays.equals(empty.shape().toArray(), arr));
+    }
+
+    @Test
+    public void testComparison(){
+        NDArray nd = new NDArray(new float[]{1.0f, 2.0f, 3.0f}, new Shape(new int[]{3}), new Context("cpu", 0));
+        NDArray nd2 = new NDArray(new float[]{3.0f, 4.0f, 5.0f}, new Shape(new int[]{3}), new Context("cpu", 0));
+        nd = nd.add(nd2);
+        float[] greater = new float[]{1, 1, 1};
+        assertTrue(Arrays.equals(nd.greater(nd2).toArray(), greater));
+        nd = nd.subtract(nd2);
+        nd = nd.subtract(nd2);
+        float[] lesser = new float[]{0, 0, 0};
+        assertTrue(Arrays.equals(nd.greater(nd2).toArray(), lesser));
+    }
+
+    @Test
+    public void testGenerated(){
+        NDArray$ NDArray = NDArray$.MODULE$;
+        float[] arr = new float[]{1.0f, 2.0f, 3.0f};
+        NDArray nd = new NDArray(arr, new Shape(new int[]{3}), new Context("cpu", 0));
+        float result = NDArray.norm(NDArray.new normParam(nd))[0].toArray()[0];
+        float cal = 0.0f;
+        for (float ele : arr) {
+            cal += ele * ele;
+        }
+        cal = (float) Math.sqrt(cal);
+        assertTrue(Math.abs(result - cal) < 1e-5);
+        NDArray dotResult = new NDArray(new float[]{0}, new Shape(new int[]{1}), new Context("cpu", 0));
+        NDArray.dot(NDArray.new dotParam(nd, nd).setOut(dotResult));
+        assertTrue(Arrays.equals(dotResult.toArray(), new float[]{14.0f}));
+    }
+}
diff --git a/scala-package/core/src/test/java/org/apache/mxnet/javaapi/ResourceScopeTestSuite.java b/scala-package/core/src/test/java/org/apache/mxnet/javaapi/ResourceScopeTestSuite.java
new file mode 100644
index 00000000000..1c246d870e2
--- /dev/null
+++ b/scala-package/core/src/test/java/org/apache/mxnet/javaapi/ResourceScopeTestSuite.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.mxnet.javaapi;
+
+import org.apache.mxnet.NativeResourceRef;
+import org.apache.mxnet.ResourceScope;
+import org.junit.Test;
+
+import java.util.*;
+import java.util.concurrent.Callable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ResourceScopeTestSuite {
+
+    /**
+     * This is a placeholder class to test out whether NDArray References get collected or not when using
+     * try-with-resources in Java.
+     *
+     */
+    class TestNDArray  {
+        NDArray selfArray;
+
+        public TestNDArray(Context context, int[] shape) {
+            this.selfArray = NDArray.ones(context, shape);
+        }
+
+        public boolean verifyIsDisposed() {
+            return this.selfArray.nd().isDisposed();
+        }
+
+        public NativeResourceRef getNDArrayReference() {
+            return this.selfArray.nd().ref();
+        }
+    }
+
+    @Test
+    public void testNDArrayAutoRelease() {
+        TestNDArray test = null;
+
+        try (ResourceScope scope = new ResourceScope()) {
+            test = new TestNDArray(Context.cpu(), new int[]{100, 100});
+        }
+
+        assertTrue(test.verifyIsDisposed());
+    }
+
+    @Test
+    public void testObjectReleaseFromList() {
+        List<TestNDArray> list = new ArrayList<>();
+
+        try (ResourceScope scope = new ResourceScope()) {
+            for (int i = 0;i < 10; i++) {
+                list.add(new TestNDArray(Context.cpu(), new int[] {100, 100}));
+            }
+        }
+
+        assertEquals(list.size() , 10);
+        for (TestNDArray item : list) {
+            assertTrue(item.verifyIsDisposed());
+        }
+    }
+
+    @Test
+    public void testObjectReleaseFromMap() {
+        Map<String, TestNDArray> stringToNDArrayMap = new HashMap<>();
+
+        try (ResourceScope scope = new ResourceScope()) {
+            for (int i = 0;i < 10; i++) {
+                stringToNDArrayMap.put(String.valueOf(i),new TestNDArray(Context.cpu(), new int[] {i, i}));
+            }
+        }
+
+        assertEquals(stringToNDArrayMap.size(), 10);
+        for (Map.Entry<String, TestNDArray> entry : stringToNDArrayMap.entrySet()) {
+            assertTrue(entry.getValue().verifyIsDisposed());
+        }
+
+        Map<TestNDArray, String> ndArrayToStringMap = new HashMap<>();
+
+        try (ResourceScope scope = new ResourceScope()) {
+            for (int i = 0;i < 10; i++) {
+                ndArrayToStringMap.put(new TestNDArray(Context.cpu(), new int[] {i, i}), String.valueOf(i));
+            }
+        }
+
+        assertEquals(ndArrayToStringMap.size(), 10);
+        for (Map.Entry<TestNDArray, String> entry : ndArrayToStringMap.entrySet()) {
+            assertTrue(entry.getKey().verifyIsDisposed());
+        }
+
+    }
+}
diff --git a/scala-package/core/src/test/scala/org/apache/mxnet/NativeResourceSuite.scala b/scala-package/core/src/test/scala/org/apache/mxnet/NativeResourceSuite.scala
new file mode 100644
index 00000000000..81a9f605a88
--- /dev/null
+++ b/scala-package/core/src/test/scala/org/apache/mxnet/NativeResourceSuite.scala
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet
+
+import java.lang.ref.ReferenceQueue
+import java.util.concurrent.ConcurrentHashMap
+
+import org.apache.mxnet.Base.CPtrAddress
+import org.mockito.Matchers.any
+import org.scalatest.{BeforeAndAfterAll, FunSuite, Matchers, TagAnnotation}
+import org.mockito.Mockito._
+
+@TagAnnotation("resource")
+class NativeResourceSuite extends FunSuite with BeforeAndAfterAll with Matchers {
+
+  object TestRef  {
+    def getRefQueue: ReferenceQueue[NativeResource] = { NativeResourceRef.refQ}
+    def getRefMap: ConcurrentHashMap[NativeResourceRef, CPtrAddress]
+    = {NativeResourceRef.refMap}
+    def getCleaner: Thread = { NativeResourceRef.cleaner }
+  }
+
+  class TestRef(resource: NativeResource,
+                          resourceDeAllocator: CPtrAddress => Int)
+    extends NativeResourceRef(resource, resourceDeAllocator) {
+  }
+
+  test(testName = "test native resource setup/teardown") {
+    val a = spy(NDArray.ones(Shape(2, 3)))
+    val aRef = a.ref
+    val spyRef = spy(aRef)
+
+    assert(TestRef.getRefMap.containsKey(aRef) == true)
+    a.close()
+    verify(a).dispose()
+    verify(a).nativeDeAllocator
+    // resourceDeAllocator does not get called when explicitly closing
+    verify(spyRef, times(0)).resourceDeAllocator
+
+    assert(TestRef.getRefMap.containsKey(aRef) == false)
+    assert(a.isDisposed == true, "isDisposed should be set to true after calling close")
+  }
+
+  test(testName = "test dispose") {
+    val a: NDArray = spy(NDArray.ones(Shape(3, 4)))
+    val aRef = a.ref
+    val spyRef = spy(aRef)
+    a.dispose()
+    verify(a).nativeDeAllocator
+    assert(TestRef.getRefMap.containsKey(aRef) == false)
+    assert(a.isDisposed == true, "isDisposed should be set to true after calling close")
+  }
+}
+
diff --git a/scala-package/core/src/test/scala/org/apache/mxnet/ResourceScopeSuite.scala b/scala-package/core/src/test/scala/org/apache/mxnet/ResourceScopeSuite.scala
new file mode 100644
index 00000000000..41dfa7d0ead
--- /dev/null
+++ b/scala-package/core/src/test/scala/org/apache/mxnet/ResourceScopeSuite.scala
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet
+
+import java.lang.ref.ReferenceQueue
+import java.util.concurrent.ConcurrentHashMap
+
+import org.apache.mxnet.Base.CPtrAddress
+import org.apache.mxnet.ResourceScope.logger
+import org.mockito.Matchers.any
+import org.scalatest.{BeforeAndAfterAll, FunSuite, Matchers}
+import org.mockito.Mockito._
+import scala.collection.mutable.HashMap
+
+class ResourceScopeSuite extends FunSuite with BeforeAndAfterAll with Matchers {
+
+  class TestNativeResource extends NativeResource {
+    /**
+      * native Address associated with this object
+      */
+    override def nativeAddress: CPtrAddress = hashCode()
+
+    /**
+      * Function Pointer to the NativeDeAllocator of nativeAddress
+      */
+    override def nativeDeAllocator: CPtrAddress => Int = TestNativeResource.deAllocator
+
+    /** Call NativeResource.register to get the reference
+      */
+    override val ref: NativeResourceRef = super.register()
+    /**
+      * Off-Heap Bytes Allocated for this object
+      */
+    override val bytesAllocated: Long = 0
+  }
+  object TestNativeResource {
+    def deAllocator(handle: CPtrAddress): Int = 0
+  }
+
+  object TestPhantomRef  {
+    def getRefQueue: ReferenceQueue[NativeResource] = { NativeResourceRef.refQ}
+    def getRefMap: ConcurrentHashMap[NativeResourceRef, CPtrAddress]
+    = {NativeResourceRef.refMap}
+    def getCleaner: Thread = { NativeResourceRef.cleaner }
+
+  }
+
+  class TestPhantomRef(resource: NativeResource,
+                       resourceDeAllocator: CPtrAddress => Int)
+    extends NativeResourceRef(resource, resourceDeAllocator) {
+  }
+
+  test(testName = "test NDArray Auto Release") {
+    var a: NDArray = null
+    var aRef: NativeResourceRef = null
+    var b: NDArray = null
+
+    ResourceScope.using() {
+      b = ResourceScope.using() {
+          a = NDArray.ones(Shape(3, 4))
+          aRef = a.ref
+          val x = NDArray.ones(Shape(3, 4))
+        x
+      }
+      val bRef: NativeResourceRef = b.ref
+      assert(a.isDisposed == true,
+        "objects created within scope should have isDisposed set to true")
+      assert(b.isDisposed == false,
+        "returned NativeResource should not be released")
+      assert(TestPhantomRef.getRefMap.containsKey(aRef) == false,
+        "reference of resource in Scope should be removed refMap")
+      assert(TestPhantomRef.getRefMap.containsKey(bRef) == true,
+        "reference of resource outside scope should be not removed refMap")
+    }
+    assert(b.isDisposed, "resource returned from inner scope should be released in outer scope")
+  }
+
+  test("test return object release from outer scope") {
+    var a: TestNativeResource = null
+    ResourceScope.using() {
+      a = ResourceScope.using() {
+        new TestNativeResource()
+      }
+      assert(a.isDisposed == false, "returned object should not be disposed within Using")
+    }
+    assert(a.isDisposed == true, "returned object should be disposed in the outer scope")
+  }
+
+  test(testName = "test NativeResources in returned Lists are not disposed") {
+    var ndListRet: IndexedSeq[TestNativeResource] = null
+    ResourceScope.using() {
+      ndListRet = ResourceScope.using() {
+        val ndList: IndexedSeq[TestNativeResource] =
+          IndexedSeq(new TestNativeResource(), new TestNativeResource())
+        ndList
+      }
+      ndListRet.foreach(nd => assert(nd.isDisposed == false,
+        "NativeResources within a returned collection should not be disposed"))
+    }
+    ndListRet.foreach(nd => assert(nd.isDisposed == true,
+    "NativeResources returned from inner scope should be disposed in outer scope"))
+  }
+
+  test("test native resource inside a map") {
+    var nRInKeyOfMap: HashMap[TestNativeResource, String] = null
+    var nRInValOfMap: HashMap[String, TestNativeResource] = HashMap[String, TestNativeResource]()
+
+    ResourceScope.using() {
+      nRInKeyOfMap = ResourceScope.using() {
+        val ret = HashMap[TestNativeResource, String]()
+        ret.put(new TestNativeResource, "hello")
+        ret
+      }
+      assert(!nRInKeyOfMap.isEmpty)
+
+      nRInKeyOfMap.keysIterator.foreach(it => assert(it.isDisposed == false,
+      "NativeResources returned in Traversable should not be disposed"))
+    }
+
+    nRInKeyOfMap.keysIterator.foreach(it => assert(it.isDisposed))
+
+    ResourceScope.using() {
+
+      nRInValOfMap = ResourceScope.using() {
+        val ret = HashMap[String, TestNativeResource]()
+        ret.put("world!", new TestNativeResource)
+        ret
+      }
+      assert(!nRInValOfMap.isEmpty)
+      nRInValOfMap.valuesIterator.foreach(it => assert(it.isDisposed == false,
+        "NativeResources returned in Collection should not be disposed"))
+    }
+    nRInValOfMap.valuesIterator.foreach(it => assert(it.isDisposed))
+  }
+
+}
diff --git a/scala-package/examples/scripts/benchmark/run_java_inference_bm.sh b/scala-package/examples/scripts/benchmark/run_java_inference_bm.sh
new file mode 100644
index 00000000000..5a468e34482
--- /dev/null
+++ b/scala-package/examples/scripts/benchmark/run_java_inference_bm.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e
+
+hw_type=cpu
+if [ "$USE_GPU" = "1" ]
+then
+    hw_type=gpu
+fi
+
+platform=linux-x86_64
+
+if [[ $OSTYPE = [darwin]* ]]
+then
+    platform=osx-x86_64
+fi
+
+MXNET_ROOT=$(cd "$(dirname $0)/../../../.."; pwd)
+CLASS_PATH=$MXNET_ROOT/scala-package/assembly/$platform-$hw_type/target/*:$MXNET_ROOT/scala-package/examples/target/*
+
+java -Xmx8G -Dmxnet.traceLeakedObjects=true -cp $CLASS_PATH \
+	org.apache.mxnetexamples.javaapi.benchmark.JavaBenchmark $@
+
diff --git a/scala-package/examples/scripts/infer/objectdetector/run_ssd_example.sh b/scala-package/examples/scripts/infer/objectdetector/run_ssd_example.sh
index 8cea892b580..adb8830de06 100755
--- a/scala-package/examples/scripts/infer/objectdetector/run_ssd_example.sh
+++ b/scala-package/examples/scripts/infer/objectdetector/run_ssd_example.sh
@@ -17,9 +17,21 @@
 # specific language governing permissions and limitations
 # under the License.
 
+hw_type=cpu
+if [[ $1 = gpu ]]
+then
+    hw_type=gpu
+fi
+
+platform=linux-x86_64
+
+if [[ $OSTYPE = [darwin]* ]]
+then
+    platform=osx-x86_64
+fi
 
 MXNET_ROOT=$(cd "$(dirname $0)/../../../../../"; pwd)
-CLASS_PATH=$MXNET_ROOT/scala-package/assembly/osx-x86_64-cpu/target/*:$MXNET_ROOT/scala-package/examples/target/*:$MXNET_ROOT/scala-package/examples/target/classes/lib/*:$MXNET_ROOT/scala-package/infer/target/*
+CLASS_PATH=$MXNET_ROOT/scala-package/assembly/$platform-$hw_type/target/*:$MXNET_ROOT/scala-package/examples/target/*:$MXNET_ROOT/scala-package/examples/target/classes/lib/*:$MXNET_ROOT/scala-package/infer/target/*
 
 # model dir and prefix
 MODEL_DIR=$1
diff --git a/scala-package/examples/scripts/infer/objectdetector/run_ssd_java_example.sh b/scala-package/examples/scripts/infer/objectdetector/run_ssd_java_example.sh
new file mode 100755
index 00000000000..00ed793a7bb
--- /dev/null
+++ b/scala-package/examples/scripts/infer/objectdetector/run_ssd_java_example.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+hw_type=cpu
+if [[ $4 = gpu ]]
+then
+    hw_type=gpu
+fi
+
+platform=linux-x86_64
+
+if [[ $OSTYPE = [darwin]* ]]
+then
+    platform=osx-x86_64
+fi
+
+MXNET_ROOT=$(cd "$(dirname $0)/../../../../../"; pwd)
+CLASS_PATH=$MXNET_ROOT/scala-package/assembly/$platform-$hw_type/target/*:$MXNET_ROOT/scala-package/examples/target/*:$MXNET_ROOT/scala-package/examples/target/classes/lib/*:$MXNET_ROOT/scala-package/infer/target/*:$MXNET_ROOT/scala-package/examples/src/main/scala/org/apache/mxnetexamples/api/java/infer/imageclassifier/*
+
+# model dir and prefix
+MODEL_DIR=$1
+# input image
+INPUT_IMG=$2
+# which input image dir
+INPUT_DIR=$3
+
+java -Xmx8G -cp $CLASS_PATH \
+	org.apache.mxnetexamples.javaapi.infer.objectdetector.SSDClassifierExample \
+	--model-path-prefix $MODEL_DIR \
+	--input-image $INPUT_IMG \
+	--input-dir $INPUT_DIR
diff --git a/scala-package/examples/scripts/run_train_mnist.sh b/scala-package/examples/scripts/run_train_mnist.sh
new file mode 100755
index 00000000000..ea53c1ade66
--- /dev/null
+++ b/scala-package/examples/scripts/run_train_mnist.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e
+
+MXNET_ROOT=$(cd "$(dirname $0)/../../.."; pwd)
+echo $MXNET_ROOT
+CLASS_PATH=$MXNET_ROOT/scala-package/assembly/linux-x86_64-cpu/target/*:$MXNET_ROOT/scala-package/examples/target/*:$MXNET_ROOT/scala-package/examples/target/classes/lib/*:$MXNET_ROOT/scala-package/infer/target/*
+
+# model dir
+DATA_PATH=$2
+
+java -XX:+PrintGC -Xms256M -Xmx512M -Dmxnet.traceLeakedObjects=false -cp $CLASS_PATH \
+        org.apache.mxnetexamples.imclassification.TrainMnist \
+        --data-dir /home/ubuntu/mxnet_scala/scala-package/examples/mnist/ \
+        --num-epochs 10000000 \
+        --batch-size 1024
\ No newline at end of file
diff --git a/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/InferBase.java b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/InferBase.java
new file mode 100644
index 00000000000..fdcde6b4152
--- /dev/null
+++ b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/InferBase.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.mxnetexamples.javaapi.benchmark;
+
+import org.apache.mxnet.javaapi.Context;
+import org.kohsuke.args4j.Option;
+
+import java.util.List;
+
+abstract class InferBase {
+    @Option(name = "--num-runs", usage = "Number of runs")
+    public int numRun = 1;
+    @Option(name = "--model-name", usage = "Name of the model")
+    public String modelName = "";
+    @Option(name = "--batchsize", usage = "Size of the batch")
+    public int batchSize = 1;
+
+    public abstract void preProcessModel(List<Context> context);
+    public abstract void runSingleInference();
+    public abstract void runBatchInference();
+}
diff --git a/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/JavaBenchmark.java b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/JavaBenchmark.java
new file mode 100644
index 00000000000..1baca20fbe6
--- /dev/null
+++ b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/JavaBenchmark.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnetexamples.javaapi.benchmark;
+
+import org.apache.mxnet.javaapi.Context;
+import org.kohsuke.args4j.CmdLineParser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class JavaBenchmark {
+
+    private static boolean runBatch = false;
+
+    private static void parse(Object inst, String[] args) {
+        CmdLineParser parser  = new CmdLineParser(inst);
+        try {
+            parser.parseArgument(args);
+        } catch (Exception e) {
+            System.err.println(e.getMessage() + e);
+            parser.printUsage(System.err);
+            System.exit(1);
+        }
+    }
+
+    private static long percentile(int p, long[] seq) {
+        Arrays.sort(seq);
+        int k = (int) Math.ceil((seq.length - 1) * (p / 100.0));
+        return seq[k];
+    }
+
+    private static void printStatistics(long[] inferenceTimesRaw, String metricsPrefix)  {
+        long[] inferenceTimes = inferenceTimesRaw;
+        // remove head and tail
+        if (inferenceTimes.length > 2) {
+            inferenceTimes = Arrays.copyOfRange(inferenceTimesRaw,
+                    1, inferenceTimesRaw.length - 1);
+        }
+        double p50 = percentile(50, inferenceTimes) / 1.0e6;
+        double p99 = percentile(99, inferenceTimes) / 1.0e6;
+        double p90 = percentile(90, inferenceTimes) / 1.0e6;
+        long sum = 0;
+        for (long time: inferenceTimes) sum += time;
+        double average = sum / (inferenceTimes.length * 1.0e6);
+
+        System.out.println(
+                String.format("\n%s_p99 %fms\n%s_p90 %fms\n%s_p50 %fms\n%s_average %1.2fms",
+                        metricsPrefix, p99, metricsPrefix, p90,
+                        metricsPrefix, p50, metricsPrefix, average)
+        );
+
+    }
+
+    private static List<Context> getContext()  {
+        List<Context> context = new ArrayList<Context>();
+        if (System.getenv().containsKey("SCALA_TEST_ON_GPU") &&
+                Integer.valueOf(System.getenv("SCALA_TEST_ON_GPU")) == 1) {
+            context.add(Context.gpu());
+        } else {
+            context.add(Context.cpu());
+        }
+        return context;
+    }
+
+    public static void main(String[] args) {
+        if (args.length < 2) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Please follow the format:");
+            sb.append("\n  --model-name <model-name>");
+            sb.append("\n  --num-runs <number of runs>");
+            sb.append("\n  --batchsize <batch size>");
+            System.out.println(sb.toString());
+            return;
+        }
+        String modelName = args[1];
+        InferBase model = null;
+        switch(modelName) {
+            case "ObjectDetection":
+                runBatch = true;
+                ObjectDetectionBenchmark inst = new ObjectDetectionBenchmark();
+                parse(inst, args);
+                model = inst;
+            default:
+                System.err.println("Model name not found! " + modelName);
+                System.exit(1);
+        }
+        List<Context> context = getContext();
+        if (System.getenv().containsKey("SCALA_TEST_ON_GPU") &&
+                Integer.valueOf(System.getenv("SCALA_TEST_ON_GPU")) == 1) {
+            context.add(Context.gpu());
+        } else {
+            context.add(Context.cpu());
+        }
+
+        long[] result = new long[model.numRun];
+        model.preProcessModel(context);
+        if (runBatch) {
+            for (int i =0;i < model.numRun; i++) {
+                long currTime = System.nanoTime();
+                model.runBatchInference();
+                result[i] = System.nanoTime() - currTime;
+            }
+            System.out.println("Batchsize: " + model.batchSize);
+            System.out.println("Num of runs: " + model.numRun);
+            printStatistics(result, modelName +"batch_inference");
+        }
+
+        model.batchSize = 1;
+        model.preProcessModel(context);
+        result = new long[model.numRun];
+        for (int i = 0; i < model.numRun; i++) {
+            long currTime = System.nanoTime();
+            model.runSingleInference();
+            result[i] = System.nanoTime() - currTime;
+        }
+        System.out.println("Num of runs: " + model.numRun);
+        printStatistics(result, modelName + "single_inference");
+    }
+}
diff --git a/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/ObjectDetectionBenchmark.java b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/ObjectDetectionBenchmark.java
new file mode 100644
index 00000000000..257ea324162
--- /dev/null
+++ b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/benchmark/ObjectDetectionBenchmark.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnetexamples.javaapi.benchmark;
+
+import org.apache.mxnet.infer.javaapi.ObjectDetector;
+import org.apache.mxnet.javaapi.*;
+import org.kohsuke.args4j.Option;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class ObjectDetectionBenchmark extends InferBase {
+    @Option(name = "--model-path-prefix", usage = "input model directory and prefix of the model")
+    public String modelPathPrefix = "/model/ssd_resnet50_512";
+    @Option(name = "--input-image", usage = "the input image")
+    public String inputImagePath = "/images/dog.jpg";
+
+    private ObjectDetector objDet;
+    private NDArray img;
+    private NDArray$ NDArray = NDArray$.MODULE$;
+
+    public void preProcessModel(List<Context> context) {
+        Shape inputShape = new Shape(new int[] {this.batchSize, 3, 512, 512});
+        List<DataDesc> inputDescriptors = new ArrayList<>();
+        inputDescriptors.add(new DataDesc("data", inputShape, DType.Float32(), "NCHW"));
+        objDet = new ObjectDetector(modelPathPrefix, inputDescriptors, context, 0);
+        img = ObjectDetector.bufferedImageToPixels(
+                ObjectDetector.reshapeImage(
+                    ObjectDetector.loadImageFromFile(inputImagePath), 512, 512
+                ),
+                new Shape(new int[] {1, 3, 512, 512})
+        );
+    }
+
+    public void runSingleInference() {
+        List<NDArray> nd = new ArrayList<>();
+        nd.add(img);
+        objDet.objectDetectWithNDArray(nd, 3);
+    }
+
+    public void runBatchInference() {
+        List<NDArray> nd = new ArrayList<>();
+        NDArray[] temp = new NDArray[batchSize];
+        for (int i = 0; i < batchSize; i++) temp[i] = img.copy();
+        NDArray batched = NDArray.concat(temp, batchSize, 0, null)[0];
+        nd.add(batched);
+        objDet.objectDetectWithNDArray(nd, 3);
+    }
+}
diff --git a/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/infer/objectdetector/README.md b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/infer/objectdetector/README.md
new file mode 100644
index 00000000000..63b9f929a82
--- /dev/null
+++ b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/infer/objectdetector/README.md
@@ -0,0 +1,116 @@
+# Single Shot Multi Object Detection using Scala Inference API
+
+In this example, you will learn how to use Scala Inference API to run Inference on pre-trained Single Shot Multi Object Detection (SSD) MXNet model.
+
+The model is trained on the [Pascal VOC 2012 dataset](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html). The network is a SSD model built on Resnet50 as base network to extract image features. The model is trained to detect the following entities (classes): ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']. For more details about the model, you can refer to the [MXNet SSD example](https://github.com/apache/incubator-mxnet/tree/master/example/ssd).
+
+
+## Contents
+
+1. [Prerequisites](#prerequisites)
+2. [Download artifacts](#download-artifacts)
+3. [Setup datapath and parameters](#setup-datapath-and-parameters)
+4. [Run the image inference example](#run-the-image-inference-example)
+5. [Infer APIs](#infer-api-details)
+6. [Next steps](#next-steps)
+
+
+## Prerequisites
+
+1. MXNet
+2. MXNet Scala Package
+3. [IntelliJ IDE (or alternative IDE) project setup](http://mxnet.incubator.apache.org/tutorials/scala/mxnet_scala_on_intellij.html) with the MXNet Scala Package
+4. wget
+
+
+## Setup Guide
+
+### Download Artifacts
+#### Step 1
+You can download the files using the script `get_ssd_data.sh`. It will download and place the model files in a `model` folder and the test image files in a `image` folder in the current directory.
+From the `scala-package/examples/scripts/infer/imageclassifier/` folder run:
+
+```bash
+./get_ssd_data.sh
+```
+
+**Note**: You may need to run `chmod +x get_resnet_data.sh` before running this script.
+
+Alternatively use the following links to download the Symbol and Params files via your browser:
+- [resnet50_ssd_model-symbol.json](https://s3.amazonaws.com/model-server/models/resnet50_ssd/resnet50_ssd_model-symbol.json)
+- [resnet50_ssd_model-0000.params](https://s3.amazonaws.com/model-server/models/resnet50_ssd/resnet50_ssd_model-0000.params)
+- [synset.txt](https://github.com/awslabs/mxnet-model-server/blob/master/examples/ssd/synset.txt)
+
+In the pre-trained model, the `input_name` is `data` and shape is `(1, 3, 512, 512)`.
+This shape translates to: a batch of `1` image, the image has color and uses `3` channels (RGB), and the image has the dimensions of `512` pixels in height by `512` pixels in width.
+
+`image/jpeg` is the expected input type, since this example's image pre-processor only supports the handling of binary JPEG images.
+
+The output shape is `(1, 6132, 6)`. As with the input, the `1` is the number of images. `6132` is the number of prediction results, and `6` is for the size of each prediction. Each prediction contains the following components:
+- `Class`
+- `Accuracy`
+- `Xmin`
+- `Ymin`
+- `Xmax`
+- `Ymax`
+
+
+### Setup Datapath and Parameters
+#### Step 2
+The code `Line 31: val baseDir = System.getProperty("user.dir")` in the example will automatically searches the work directory you have defined. Please put the files in your [work directory](https://stackoverflow.com/questions/16239130/java-user-dir-property-what-exactly-does-it-mean). <!-- how do you define the work directory? -->
+
+Alternatively, if you would like to use your own path, please change line 31 into your own path
+```scala
+val baseDir = <Your Own Path>
+```
+
+The followings is the parameters defined for this example, you can find more information in the `class SSDClassifierExample`.
+
+| Argument                      | Comments                                 |
+| ----------------------------- | ---------------------------------------- |
+| `model-path-prefix`                   | Folder path with prefix to the model (including json, params, and any synset file). |
+| `input-image`                 | The image to run inference on. |
+| `input-dir`                   | The directory of images to run inference on. |
+
+
+## How to Run Inference
+After the previous steps, you should be able to run the code using the following script that will pass all of the required parameters to the Infer API.
+
+From the `scala-package/examples/scripts/inferexample/objectdetector/` folder run:
+
+```bash
+./run_ssd_example.sh ../models/resnet50_ssd/resnet50_ssd/resnet50_ssd_model ../images/dog.jpg ../images
+```
+
+**Notes**:
+* These are relative paths to this script.
+* You may need to run `chmod +x run_ssd_example.sh` before running this script.
+
+The example should give expected output as shown below:
+```
+Class: car
+Probabilties: 0.99847263
+(Coord:,312.21335,72.0291,456.01443,150.66176)
+Class: bicycle
+Probabilties: 0.90473825
+(Coord:,155.95807,149.96362,383.8369,418.94513)
+Class: dog
+Probabilties: 0.8226818
+(Coord:,83.82353,179.13998,206.63783,476.7875)
+```
+the outputs come from the the input image, with top3 predictions picked.
+
+
+## Infer API Details
+This example uses ObjectDetector class provided by MXNet's scala package Infer APIs. It provides methods to load the images, create NDArray out of Java BufferedImage and run prediction using Classifier and Predictor APIs.
+
+
+## References
+This documentation used the model and inference setup guide from the [MXNet Model Server SSD example](https://github.com/awslabs/mxnet-model-server/blob/master/examples/ssd/README.md).
+
+
+## Next Steps
+
+Check out the following related tutorials and examples for the Infer API:
+
+* [Image Classification with the MXNet Scala Infer API](../imageclassifier/README.md)
diff --git a/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/infer/objectdetector/SSDClassifierExample.java b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/infer/objectdetector/SSDClassifierExample.java
new file mode 100644
index 00000000000..4befc8edde6
--- /dev/null
+++ b/scala-package/examples/src/main/java/org/apache/mxnetexamples/javaapi/infer/objectdetector/SSDClassifierExample.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnetexamples.javaapi.infer.objectdetector;
+
+import org.apache.mxnet.infer.javaapi.ObjectDetectorOutput;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.Option;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.mxnet.javaapi.*;
+import org.apache.mxnet.infer.javaapi.ObjectDetector;
+
+// scalastyle:off
+import java.awt.image.BufferedImage;
+// scalastyle:on
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import java.io.File;
+
+public class SSDClassifierExample {
+	@Option(name = "--model-path-prefix", usage = "input model directory and prefix of the model")
+	private String modelPathPrefix = "/model/ssd_resnet50_512";
+	@Option(name = "--input-image", usage = "the input image")
+	private String inputImagePath = "/images/dog.jpg";
+	@Option(name = "--input-dir", usage = "the input batch of images directory")
+	private String inputImageDir = "/images/";
+	
+	final static Logger logger = LoggerFactory.getLogger(SSDClassifierExample.class);
+	
+	static List<List<ObjectDetectorOutput>>
+	runObjectDetectionSingle(String modelPathPrefix, String inputImagePath, List<Context> context) {
+		Shape inputShape = new Shape(new int[] {1, 3, 512, 512});
+		List<DataDesc> inputDescriptors = new ArrayList<DataDesc>();
+		inputDescriptors.add(new DataDesc("data", inputShape, DType.Float32(), "NCHW"));
+		BufferedImage img = ObjectDetector.loadImageFromFile(inputImagePath);
+		ObjectDetector objDet = new ObjectDetector(modelPathPrefix, inputDescriptors, context, 0);
+		return objDet.imageObjectDetect(img, 3);
+	}
+	
+	static List<List<List<ObjectDetectorOutput>>>
+	runObjectDetectionBatch(String modelPathPrefix, String inputImageDir, List<Context> context) {
+		Shape inputShape = new Shape(new int[]{1, 3, 512, 512});
+		List<DataDesc> inputDescriptors = new ArrayList<DataDesc>();
+		inputDescriptors.add(new DataDesc("data", inputShape, DType.Float32(), "NCHW"));
+		ObjectDetector objDet = new ObjectDetector(modelPathPrefix, inputDescriptors, context, 0);
+		
+		// Loading batch of images from the directory path
+		List<List<String>> batchFiles = generateBatches(inputImageDir, 20);
+		List<List<List<ObjectDetectorOutput>>> outputList
+				= new ArrayList<List<List<ObjectDetectorOutput>>>();
+		
+		for (List<String> batchFile : batchFiles) {
+			List<BufferedImage> imgList = ObjectDetector.loadInputBatch(batchFile);
+			// Running inference on batch of images loaded in previous step
+			List<List<ObjectDetectorOutput>> tmp
+					= objDet.imageBatchObjectDetect(imgList, 5);
+			outputList.add(tmp);
+		}
+		return outputList;
+	}
+	
+	static List<List<String>> generateBatches(String inputImageDirPath, int batchSize) {
+		File dir = new File(inputImageDirPath);
+
+		List<List<String>> output = new ArrayList<List<String>>();
+		List<String> batch = new ArrayList<String>();
+		for (File imgFile : dir.listFiles()) {
+			batch.add(imgFile.getPath());
+			if (batch.size() == batchSize) {
+				output.add(batch);
+				batch = new ArrayList<String>();
+			}
+		}
+		if (batch.size() > 0) {
+			output.add(batch);
+		}
+		return output;
+	}
+	
+	public static void main(String[] args) {
+		SSDClassifierExample inst = new SSDClassifierExample();
+		CmdLineParser parser  = new CmdLineParser(inst);
+		try {
+			parser.parseArgument(args);
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+			parser.printUsage(System.err);
+			System.exit(1);
+		}
+		
+		String mdprefixDir = inst.modelPathPrefix;
+		String imgPath = inst.inputImagePath;
+		String imgDir = inst.inputImageDir;
+		
+		if (!checkExist(Arrays.asList(mdprefixDir + "-symbol.json", imgDir, imgPath))) {
+			logger.error("Model or input image path does not exist");
+			System.exit(1);
+		}
+		
+		List<Context> context = new ArrayList<Context>();
+		if (System.getenv().containsKey("SCALA_TEST_ON_GPU") &&
+				Integer.valueOf(System.getenv("SCALA_TEST_ON_GPU")) == 1) {
+			context.add(Context.gpu());
+		} else {
+			context.add(Context.cpu());
+		}
+		
+		try {
+			Shape inputShape = new Shape(new int[] {1, 3, 512, 512});
+			Shape outputShape = new Shape(new int[] {1, 6132, 6});
+			
+			
+			int width = inputShape.get(2);
+			int height = inputShape.get(3);
+			String outputStr = "\n";
+			
+			List<List<ObjectDetectorOutput>> output
+					= runObjectDetectionSingle(mdprefixDir, imgPath, context);
+			
+			for (List<ObjectDetectorOutput> ele : output) {
+				for (ObjectDetectorOutput i : ele) {
+					outputStr += "Class: " + i.getClassName() + "\n";
+					outputStr += "Probabilties: " + i.getProbability() + "\n";
+					
+					List<Float> coord = Arrays.asList(i.getXMin() * width,
+							i.getXMax() * height, i.getYMin() * width, i.getYMax() * height);
+					StringBuilder sb = new StringBuilder();
+					for (float c: coord) {
+						sb.append(", ").append(c);
+					}
+					outputStr += "Coord:" + sb.substring(2)+ "\n";
+				}
+			}
+			logger.info(outputStr);
+			
+			List<List<List<ObjectDetectorOutput>>> outputList =
+					runObjectDetectionBatch(mdprefixDir, imgDir, context);
+			
+			outputStr = "\n";
+			int index = 0;
+			for (List<List<ObjectDetectorOutput>> i: outputList) {
+				for (List<ObjectDetectorOutput> j : i) {
+					outputStr += "*** Image " + (index + 1) + "***" + "\n";
+					for (ObjectDetectorOutput k : j) {
+						outputStr += "Class: " + k.getClassName() + "\n";
+						outputStr += "Probabilties: " + k.getProbability() + "\n";
+						List<Float> coord = Arrays.asList(k.getXMin() * width,
+								k.getXMax() * height, k.getYMin() * width, k.getYMax() * height);
+						
+						StringBuilder sb = new StringBuilder();
+						for (float c : coord) {
+							sb.append(", ").append(c);
+						}
+						outputStr += "Coord:" + sb.substring(2) + "\n";
+					}
+					index++;
+				}
+			}
+			logger.info(outputStr);
+			
+		} catch (Exception e) {
+			logger.error(e.getMessage(), e);
+			parser.printUsage(System.err);
+			System.exit(1);
+		}
+		System.exit(0);
+	}
+	
+	static Boolean checkExist(List<String> arr)  {
+		Boolean exist = true;
+		for (String item : arr) {
+			exist = new File(item).exists() && exist;
+			if (!exist) {
+				logger.error("Cannot find: " + item);
+			}
+		}
+		return exist;
+	}
+}
diff --git a/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/TrainModel.scala b/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/TrainModel.scala
index 608e191e019..f6c283c3dfb 100644
--- a/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/TrainModel.scala
+++ b/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/TrainModel.scala
@@ -43,7 +43,7 @@ object TrainModel {
     */
   def test(model: String, dataPath: String, numExamples: Int = 60000,
            numEpochs: Int = 10, benchmark: Boolean = false): Float = {
-    NDArrayCollector.auto().withScope {
+    ResourceScope.using() {
       val devs = Array(Context.cpu(0))
       val envs: mutable.Map[String, String] = mutable.HashMap.empty[String, String]
       val (dataLoader, net) = dataLoaderAndModel("mnist", model, dataPath,
@@ -110,44 +110,46 @@ object TrainModel {
     val inst = new TrainModel
     val parser: CmdLineParser = new CmdLineParser(inst)
     try {
-      parser.parseArgument(args.toList.asJava)
-
-      val dataPath = if (inst.dataDir == null) System.getenv("MXNET_HOME")
-      else inst.dataDir
-
-      val (dataLoader, net) = dataLoaderAndModel(inst.dataset, inst.network, dataPath,
-        inst.numLayers, inst.numExamples, inst.benchmark)
-
-      val devs =
-        if (inst.gpus != null) inst.gpus.split(',').map(id => Context.gpu(id.trim.toInt))
-        else if (inst.cpus != null) inst.cpus.split(',').map(id => Context.cpu(id.trim.toInt))
-        else Array(Context.cpu(0))
-
-      val envs: mutable.Map[String, String] = mutable.HashMap.empty[String, String]
-      envs.put("DMLC_ROLE", inst.role)
-      if (inst.schedulerHost != null) {
-        require(inst.schedulerPort > 0, "scheduler port not specified")
-        envs.put("DMLC_PS_ROOT_URI", inst.schedulerHost)
-        envs.put("DMLC_PS_ROOT_PORT", inst.schedulerPort.toString)
-        require(inst.numWorker > 0, "Num of workers must > 0")
-        envs.put("DMLC_NUM_WORKER", inst.numWorker.toString)
-        require(inst.numServer > 0, "Num of servers must > 0")
-        envs.put("DMLC_NUM_SERVER", inst.numServer.toString)
-        logger.info("Init PS environments")
-        KVStoreServer.init(envs.toMap)
-      }
-
-      if (inst.role != "worker") {
-        logger.info("Start KVStoreServer for scheduler & servers")
-        KVStoreServer.start()
-      } else {
-        Trainer.fit(batchSize = inst.batchSize, numExamples = inst.numExamples, devs = devs,
-          network = net, dataLoader = dataLoader,
-          kvStore = inst.kvStore, numEpochs = inst.numEpochs,
-          modelPrefix = inst.modelPrefix, loadEpoch = inst.loadEpoch,
-          lr = inst.lr, lrFactor = inst.lrFactor, lrFactorEpoch = inst.lrFactorEpoch,
-          monitorSize = inst.monitor)
-        logger.info("Finish fit ...")
+      ResourceScope.using() {
+        parser.parseArgument(args.toList.asJava)
+
+        val dataPath = if (inst.dataDir == null) System.getenv("MXNET_HOME")
+        else inst.dataDir
+
+        val (dataLoader, net) = dataLoaderAndModel(inst.dataset, inst.network, dataPath,
+          inst.numLayers, inst.numExamples, inst.benchmark)
+
+        val devs =
+          if (inst.gpus != null) inst.gpus.split(',').map(id => Context.gpu(id.trim.toInt))
+          else if (inst.cpus != null) inst.cpus.split(',').map(id => Context.cpu(id.trim.toInt))
+          else Array(Context.cpu(0))
+
+        val envs: mutable.Map[String, String] = mutable.HashMap.empty[String, String]
+        envs.put("DMLC_ROLE", inst.role)
+        if (inst.schedulerHost != null) {
+          require(inst.schedulerPort > 0, "scheduler port not specified")
+          envs.put("DMLC_PS_ROOT_URI", inst.schedulerHost)
+          envs.put("DMLC_PS_ROOT_PORT", inst.schedulerPort.toString)
+          require(inst.numWorker > 0, "Num of workers must > 0")
+          envs.put("DMLC_NUM_WORKER", inst.numWorker.toString)
+          require(inst.numServer > 0, "Num of servers must > 0")
+          envs.put("DMLC_NUM_SERVER", inst.numServer.toString)
+          logger.info("Init PS environments")
+          KVStoreServer.init(envs.toMap)
+        }
+
+        if (inst.role != "worker") {
+          logger.info("Start KVStoreServer for scheduler & servers")
+          KVStoreServer.start()
+        } else {
+          Trainer.fit(batchSize = inst.batchSize, numExamples = inst.numExamples, devs = devs,
+            network = net, dataLoader = dataLoader,
+            kvStore = inst.kvStore, numEpochs = inst.numEpochs,
+            modelPrefix = inst.modelPrefix, loadEpoch = inst.loadEpoch,
+            lr = inst.lr, lrFactor = inst.lrFactor, lrFactorEpoch = inst.lrFactorEpoch,
+            monitorSize = inst.monitor)
+          logger.info("Finish fit ...")
+        }
       }
     } catch {
       case ex: Exception => {
diff --git a/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/util/Trainer.scala b/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/util/Trainer.scala
index 9a54e58b653..276816cf8c8 100644
--- a/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/util/Trainer.scala
+++ b/scala-package/examples/src/main/scala/org/apache/mxnetexamples/imclassification/util/Trainer.scala
@@ -50,83 +50,84 @@ object Trainer {
           lr: Float = 0.1f, lrFactor: Float = 1f, lrFactorEpoch: Float = 1f,
           clipGradient: Float = 0f, monitorSize: Int = -1): Accuracy = {
     // kvstore
-    var kv = KVStore.create(kvStore)
+    ResourceScope.using()  {
+      var kv = KVStore.create(kvStore)
 
-    // load model
-    val modelPrefixWithRank =
-      if (modelPrefix == null) null
-      else modelPrefix + s"-${kv.rank}"
+      // load model
+      val modelPrefixWithRank =
+        if (modelPrefix == null) null
+        else modelPrefix + s"-${kv.rank}"
 
-    val (argParams, auxParams, beginEpoch) =
-      if (loadEpoch >= 0) {
-        require(modelPrefixWithRank != null)
-        val tmp = FeedForward.load(modelPrefix, loadEpoch)
-        (tmp.getArgParams, tmp.getAuxParams, loadEpoch)
-      } else {
-        (null, null, 0)
-      }
+      val (argParams, auxParams, beginEpoch) =
+        if (loadEpoch >= 0) {
+          require(modelPrefixWithRank != null)
+          val tmp = FeedForward.load(modelPrefix, loadEpoch)
+          (tmp.getArgParams, tmp.getAuxParams, loadEpoch)
+        } else {
+          (null, null, 0)
+        }
 
-    // save model
-    val checkpoint: EpochEndCallback =
-      if (modelPrefix == null) null
-      else new EpochEndCallback {
-        override def invoke(epoch: Int, symbol: Symbol,
-                            argParams: Map[String, NDArray],
-                            auxStates: Map[String, NDArray]): Unit = {
-          Model.saveCheckpoint(modelPrefix, epoch + 1, symbol, argParams, auxParams)
+      // save model
+      val checkpoint: EpochEndCallback =
+        if (modelPrefix == null) null
+        else new EpochEndCallback {
+          override def invoke(epoch: Int, symbol: Symbol,
+                              argParams: Map[String, NDArray],
+                              auxStates: Map[String, NDArray]): Unit = {
+            Model.saveCheckpoint(modelPrefix, epoch + 1, symbol, argParams, auxParams)
+          }
         }
-      }
 
-    // data
-    val (train, validation) = dataLoader(batchSize, kv)
+      // data
+      val (train, validation) = dataLoader(batchSize, kv)
 
-    // train
-    val epochSize =
-      if (kvStore == "dist_sync") numExamples / batchSize / kv.numWorkers
-      else numExamples / batchSize
+      // train
+      val epochSize =
+        if (kvStore == "dist_sync") numExamples / batchSize / kv.numWorkers
+        else numExamples / batchSize
 
-    val lrScheduler =
-      if (lrFactor < 1f) {
-        new FactorScheduler(step = Math.max((epochSize * lrFactorEpoch).toInt, 1),
-                            factor = lrFactor)
-      } else {
-        null
-      }
-    val optimizer: Optimizer = new SGD(learningRate = lr,
-      lrScheduler = lrScheduler, clipGradient = clipGradient,
-      momentum = 0.9f, wd = 0.00001f)
+      val lrScheduler =
+        if (lrFactor < 1f) {
+          new FactorScheduler(step = Math.max((epochSize * lrFactorEpoch).toInt, 1),
+                              factor = lrFactor)
+        } else {
+          null
+        }
+      val optimizer: Optimizer = new SGD(learningRate = lr,
+        lrScheduler = lrScheduler, clipGradient = clipGradient,
+        momentum = 0.9f, wd = 0.00001f)
 
-    // disable kvstore for single device
-    if (kv.`type`.contains("local") && (devs.length == 1 || devs(0).deviceType != "gpu")) {
-      kv.dispose()
-      kv = null
-    }
+      // disable kvstore for single device
+      if (kv.`type`.contains("local") && (devs.length == 1 || devs(0).deviceType != "gpu")) {
+        kv.dispose()
+        kv = null
+      }
 
-    val model = new FeedForward(ctx = devs,
-                                symbol = network,
-                                numEpoch = numEpochs,
-                                optimizer = optimizer,
-                                initializer = new Xavier(factorType = "in", magnitude = 2.34f),
-                                argParams = argParams,
-                                auxParams = auxParams,
-                                beginEpoch = beginEpoch,
-                                epochSize = epochSize)
-    if (monitorSize > 0) {
-      model.setMonitor(new Monitor(monitorSize))
-    }
-    val acc = new Accuracy()
-    model.fit(trainData = train,
-              evalData = validation,
-              evalMetric = acc,
-              kvStore = kv,
-              batchEndCallback = new Speedometer(batchSize, 50),
-              epochEndCallback = checkpoint)
-    if (kv != null) {
-      kv.dispose()
+      val model = new FeedForward(ctx = devs,
+                                  symbol = network,
+                                  numEpoch = numEpochs,
+                                  optimizer = optimizer,
+                                  initializer = new Xavier(factorType = "in", magnitude = 2.34f),
+                                  argParams = argParams,
+                                  auxParams = auxParams,
+                                  beginEpoch = beginEpoch,
+                                  epochSize = epochSize)
+      if (monitorSize > 0) {
+        model.setMonitor(new Monitor(monitorSize))
+      }
+      val acc = new Accuracy()
+      model.fit(trainData = train,
+                evalData = validation,
+                evalMetric = acc,
+                kvStore = kv,
+                batchEndCallback = new Speedometer(batchSize, 50),
+                epochEndCallback = checkpoint)
+      if (kv != null) {
+        kv.dispose()
+      }
+      acc
     }
-    acc
   }
-
   // scalastyle:on parameterNum
 }
 
diff --git a/scala-package/examples/src/main/scala/org/apache/mxnetexamples/infer/objectdetector/README.md b/scala-package/examples/src/main/scala/org/apache/mxnetexamples/infer/objectdetector/README.md
index 69328a44bab..bf4a44a76d0 100644
--- a/scala-package/examples/src/main/scala/org/apache/mxnetexamples/infer/objectdetector/README.md
+++ b/scala-package/examples/src/main/scala/org/apache/mxnetexamples/infer/objectdetector/README.md
@@ -31,7 +31,7 @@ You can download the files using the script `get_ssd_data.sh`. It will download
 From the `scala-package/examples/scripts/infer/imageclassifier/` folder run:
 
 ```bash
-./get_resnet_data.sh
+./get_ssd_data.sh
 ```
 
 **Note**: You may need to run `chmod +x get_resnet_data.sh` before running this script.
@@ -79,7 +79,7 @@ After the previous steps, you should be able to run the code using the following
 From the `scala-package/examples/scripts/inferexample/objectdetector/` folder run:
 
 ```bash
-./run_ssd_example.sh ../models/resnet50_ssd_model ../images/dog.jpg ../images
+./run_ssd_example.sh ../models/resnet50_ssd/resnet50_ssd_model ../images/dog.jpg ../images
 ```
 
 **Notes**:
diff --git a/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/ObjectDetector.scala b/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/ObjectDetector.scala
new file mode 100644
index 00000000000..447518b5a89
--- /dev/null
+++ b/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/ObjectDetector.scala
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet.infer.javaapi
+
+// scalastyle:off
+import java.awt.image.BufferedImage
+// scalastyle:on
+
+import org.apache.mxnet.javaapi.{Context, DataDesc, NDArray, Shape}
+
+import scala.collection.JavaConverters
+import scala.collection.JavaConverters._
+
+/**
+  * The ObjectDetector class helps to run ObjectDetection tasks where the goal
+  * is to find bounding boxes and corresponding labels for objects in a image.
+  *
+  * @param modelPathPrefix    Path prefix from where to load the model artifacts.
+  *                           These include the symbol, parameters, and synset.txt.
+  *                           Example: file://model-dir/ssd_resnet50_512 (containing
+  *                           ssd_resnet50_512-symbol.json, ssd_resnet50_512-0000.params,
+  *                           and synset.txt)
+  * @param inputDescriptors   Descriptors defining the input node names, shape,
+  *                           layout and type parameters
+  * @param contexts           Device contexts on which you want to run inference.
+  *                           Defaults to CPU.
+  * @param epoch              Model epoch to load; defaults to 0
+  */
+class ObjectDetector(val objDetector: org.apache.mxnet.infer.ObjectDetector){
+
+  def this(modelPathPrefix: String, inputDescriptors: java.util.List[DataDesc], contexts:
+  java.util.List[Context], epoch: Int)
+  = this {
+    val informationDesc = JavaConverters.asScalaIteratorConverter(inputDescriptors.iterator)
+      .asScala.toIndexedSeq map {a => a: org.apache.mxnet.DataDesc}
+    val inContexts = (contexts.asScala.toList map {a => a: org.apache.mxnet.Context}).toArray
+    // scalastyle:off
+    new org.apache.mxnet.infer.ObjectDetector(modelPathPrefix, informationDesc, inContexts, Some(epoch))
+    // scalastyle:on
+  }
+
+  /**
+    * Detects objects and returns bounding boxes with corresponding class/label
+    *
+    * @param inputImage       Path prefix of the input image
+    * @param topK             Number of result elements to return, sorted by probability
+    * @return                 List of list of tuples of
+    *                         (class, [probability, xmin, ymin, xmax, ymax])
+    */
+  def imageObjectDetect(inputImage: BufferedImage, topK: Int):
+  java.util.List[java.util.List[ObjectDetectorOutput]] = {
+    val ret = objDetector.imageObjectDetect(inputImage, Some(topK))
+    (ret map {a => (a map {e => new ObjectDetectorOutput(e._1, e._2)}).asJava}).asJava
+  }
+
+  /**
+    * Takes input images as NDArrays. Useful when you want to perform multiple operations on
+    * the input array, or when you want to pass a batch of input images.
+    *
+    * @param input            Indexed Sequence of NDArrays
+    * @param topK             (Optional) How many top_k (sorting will be based on the last axis)
+    *                         elements to return. If not passed, returns all unsorted output.
+    * @return                 List of list of tuples of
+    *                         (class, [probability, xmin, ymin, xmax, ymax])
+    */
+  def objectDetectWithNDArray(input: java.util.List[NDArray], topK: Int):
+  java.util.List[java.util.List[ObjectDetectorOutput]] = {
+    val ret = objDetector.objectDetectWithNDArray(convert(input.asScala.toIndexedSeq), Some(topK))
+    (ret map {a => (a map {e => new ObjectDetectorOutput(e._1, e._2)}).asJava}).asJava
+  }
+
+  /**
+    * To classify batch of input images according to the provided model
+    *
+    * @param inputBatch       Input array of buffered images
+    * @param topK             Number of result elements to return, sorted by probability
+    * @return                 List of list of tuples of (class, probability)
+    */
+  def imageBatchObjectDetect(inputBatch: java.util.List[BufferedImage], topK: Int):
+      java.util.List[java.util.List[ObjectDetectorOutput]] = {
+    val ret = objDetector.imageBatchObjectDetect(inputBatch.asScala, Some(topK))
+    (ret map {a => (a map {e => new ObjectDetectorOutput(e._1, e._2)}).asJava}).asJava
+  }
+
+  def convert[B, A <% B](l: IndexedSeq[A]): IndexedSeq[B] = l map { a => a: B }
+
+}
+
+
+object ObjectDetector {
+  implicit def fromObjectDetector(OD: org.apache.mxnet.infer.ObjectDetector):
+    ObjectDetector = new ObjectDetector(OD)
+
+  implicit def toObjectDetector(jOD: ObjectDetector):
+    org.apache.mxnet.infer.ObjectDetector = jOD.objDetector
+
+  def loadImageFromFile(inputImagePath: String): BufferedImage = {
+    org.apache.mxnet.infer.ImageClassifier.loadImageFromFile(inputImagePath)
+  }
+
+  def reshapeImage(img : BufferedImage, newWidth: Int, newHeight: Int): BufferedImage = {
+    org.apache.mxnet.infer.ImageClassifier.reshapeImage(img, newWidth, newHeight)
+  }
+
+  def bufferedImageToPixels(resizedImage: BufferedImage, inputImageShape: Shape): NDArray = {
+    org.apache.mxnet.infer.ImageClassifier.bufferedImageToPixels(resizedImage, inputImageShape)
+  }
+
+  def loadInputBatch(inputImagePaths: java.util.List[String]): java.util.List[BufferedImage] = {
+    org.apache.mxnet.infer.ImageClassifier
+      .loadInputBatch(inputImagePaths.asScala.toList).toList.asJava
+  }
+}
diff --git a/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/ObjectDetectorOutput.scala b/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/ObjectDetectorOutput.scala
new file mode 100644
index 00000000000..13369c8fcef
--- /dev/null
+++ b/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/ObjectDetectorOutput.scala
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet.infer.javaapi
+
+class ObjectDetectorOutput (className: String, args: Array[Float]){
+
+  def getClassName: String = className
+
+  def getProbability: Float = args(0)
+
+  def getXMin: Float = args(1)
+
+  def getXMax: Float = args(2)
+
+  def getYMin: Float = args(3)
+
+  def getYMax: Float = args(4)
+
+}
diff --git a/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/Predictor.scala b/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/Predictor.scala
new file mode 100644
index 00000000000..3e0fcb7b507
--- /dev/null
+++ b/scala-package/infer/src/main/scala/org/apache/mxnet/infer/javaapi/Predictor.scala
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet.infer.javaapi
+
+import org.apache.mxnet.javaapi.{Context, DataDesc, NDArray}
+
+import scala.collection.JavaConverters
+import scala.collection.JavaConverters._
+
+/**
+  * Implementation of prediction routines.
+  *
+  * @param modelPathPrefix     Path prefix from where to load the model artifacts.
+  *                            These include the symbol, parameters, and synset.txt
+  *                            Example: file://model-dir/resnet-152 (containing
+  *                            resnet-152-symbol.json, resnet-152-0000.params, and synset.txt).
+  * @param inputDescriptors    Descriptors defining the input node names, shape,
+  *                            layout and type parameters
+  *                            <p>Note: If the input Descriptors is missing batchSize
+  *                            ('N' in layout), a batchSize of 1 is assumed for the model.
+  * @param contexts            Device contexts on which you want to run inference; defaults to CPU
+  * @param epoch               Model epoch to load; defaults to 0
+
+  */
+
+// JavaDoc description of class to be updated in https://issues.apache.org/jira/browse/MXNET-1178
+class Predictor(val predictor: org.apache.mxnet.infer.Predictor){
+  def this(modelPathPrefix: String, inputDescriptors: java.util.List[DataDesc],
+           contexts: java.util.List[Context], epoch: Int)
+  = this {
+    val informationDesc = JavaConverters.asScalaIteratorConverter(inputDescriptors.iterator)
+      .asScala.toIndexedSeq map {a => a: org.apache.mxnet.DataDesc}
+    val inContexts = (contexts.asScala.toList map {a => a: org.apache.mxnet.Context}).toArray
+    new org.apache.mxnet.infer.Predictor(modelPathPrefix, informationDesc, inContexts, Some(epoch))
+  }
+
+
+  /**
+    * Takes input as List of one dimensional arrays and creates the NDArray needed for inference
+    * The array will be reshaped based on the input descriptors.
+    *
+    * @param input:            A List of a one-dimensional array.
+                              An extra List is needed for when the model has more than one input.
+    * @return                  Indexed sequence array of outputs
+    */
+  def predict(input: java.util.List[java.util.List[Float]]):
+  java.util.List[java.util.List[Float]] = {
+    val in = JavaConverters.asScalaIteratorConverter(input.iterator).asScala.toIndexedSeq
+    (predictor.predict(in map {a => a.asScala.toArray}) map {b => b.toList.asJava}).asJava
+  }
+
+
+  /**
+    * Predict using NDArray as input
+    * This method is useful when the input is a batch of data
+    * Note: User is responsible for managing allocation/deallocation of input/output NDArrays.
+    *
+    * @param input       List of NDArrays
+    * @return                  Output of predictions as NDArrays
+    */
+  def predictWithNDArray(input: java.util.List[NDArray]):
+  java.util.List[NDArray] = {
+    val ret = predictor.predictWithNDArray(convert(JavaConverters
+      .asScalaIteratorConverter(input.iterator).asScala.toIndexedSeq))
+    // TODO: For some reason the implicit wasn't working here when trying to use convert.
+    // So did it this way. Needs to be figured out
+    (ret map {a => new NDArray(a)}).asJava
+  }
+
+  private def convert[B, A <% B](l: IndexedSeq[A]): IndexedSeq[B] = l map { a => a: B }
+}
diff --git a/scala-package/macros/src/main/scala/org/apache/mxnet/APIDocGenerator.scala b/scala-package/macros/src/main/scala/org/apache/mxnet/APIDocGenerator.scala
index b4efa659443..d965bdef22b 100644
--- a/scala-package/macros/src/main/scala/org/apache/mxnet/APIDocGenerator.scala
+++ b/scala-package/macros/src/main/scala/org/apache/mxnet/APIDocGenerator.scala
@@ -17,178 +17,233 @@
 
 package org.apache.mxnet
 
-import org.apache.mxnet.init.Base._
-import org.apache.mxnet.utils.CToScalaUtils
 import java.io._
 import java.security.MessageDigest
 
-import scala.collection.mutable.{ArrayBuffer, ListBuffer}
+import scala.collection.mutable.ListBuffer
 
 /**
   * This object will generate the Scala documentation of the new Scala API
   * Two file namely: SymbolAPIBase.scala and NDArrayAPIBase.scala
   * The code will be executed during Macros stage and file live in Core stage
   */
-private[mxnet] object APIDocGenerator{
-  case class absClassArg(argName : String, argType : String, argDesc : String, isOptional : Boolean)
-  case class absClassFunction(name : String, desc : String,
-                           listOfArgs: List[absClassArg], returnType : String)
+private[mxnet] object APIDocGenerator extends GeneratorBase {
 
-
-  def main(args: Array[String]) : Unit = {
+  def main(args: Array[String]): Unit = {
     val FILE_PATH = args(0)
     val hashCollector = ListBuffer[String]()
-    hashCollector += absClassGen(FILE_PATH, true)
-    hashCollector += absClassGen(FILE_PATH, false)
+    hashCollector += typeSafeClassGen(FILE_PATH, true)
+    hashCollector += typeSafeClassGen(FILE_PATH, false)
     hashCollector += nonTypeSafeClassGen(FILE_PATH, true)
     hashCollector += nonTypeSafeClassGen(FILE_PATH, false)
+    hashCollector += javaClassGen(FILE_PATH)
     val finalHash = hashCollector.mkString("\n")
   }
 
-  def MD5Generator(input : String) : String = {
+  def MD5Generator(input: String): String = {
     val md = MessageDigest.getInstance("MD5")
     md.update(input.getBytes("UTF-8"))
     val digest = md.digest()
     org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(digest)
   }
 
-  def absClassGen(FILE_PATH : String, isSymbol : Boolean) : String = {
-    // scalastyle:off
-    val absClassFunctions = getSymbolNDArrayMethods(isSymbol)
-    // Defines Operators that should not generated
-    val notGenerated = Set("Custom")
-    // TODO: Add Filter to the same location in case of refactor
-    val absFuncs = absClassFunctions.filterNot(_.name.startsWith("_"))
-      .filterNot(ele => notGenerated.contains(ele.name))
-      .map(absClassFunction => {
-      val scalaDoc = generateAPIDocFromBackend(absClassFunction)
-      val defBody = generateAPISignature(absClassFunction, isSymbol)
-      s"$scalaDoc\n$defBody"
-    })
-    val packageName = if (isSymbol) "SymbolAPIBase" else "NDArrayAPIBase"
-    val apacheLicence = "/*\n* Licensed to the Apache Software Foundation (ASF) under one or more\n* contributor license agreements.  See the NOTICE file distributed with\n* this work for additional information regarding copyright ownership.\n* The ASF licenses this file to You under the Apache License, Version 2.0\n* (the \"License\"); you may not use this file except in compliance with\n* the License.  You may obtain a copy of the License at\n*\n*    http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n"
-    val scalaStyle = "// scalastyle:off"
-    val packageDef = "package org.apache.mxnet"
-    val imports = "import org.apache.mxnet.annotation.Experimental"
-    val absClassDef = s"abstract class $packageName"
-    val finalStr = s"$apacheLicence\n$scalaStyle\n$packageDef\n$imports\n$absClassDef {\n${absFuncs.mkString("\n")}\n}"
-    val pw = new PrintWriter(new File(FILE_PATH + s"$packageName.scala"))
-    pw.write(finalStr)
-    pw.close()
-    MD5Generator(finalStr)
+  def typeSafeClassGen(FILE_PATH: String, isSymbol: Boolean): String = {
+    val generated = typeSafeFunctionsToGenerate(isSymbol, isContrib = false)
+      .map { func =>
+        val scalaDoc = generateAPIDocFromBackend(func)
+        val decl = generateAPISignature(func, isSymbol)
+        s"$scalaDoc\n$decl"
+      }
+
+    writeFile(
+      FILE_PATH,
+      if (isSymbol) "SymbolAPIBase" else "NDArrayAPIBase",
+      "package org.apache.mxnet",
+      generated)
   }
 
-  def nonTypeSafeClassGen(FILE_PATH : String, isSymbol : Boolean) : String = {
-    // scalastyle:off
-    val absClassFunctions = getSymbolNDArrayMethods(isSymbol)
-    val absFuncs = absClassFunctions.map(absClassFunction => {
-      val scalaDoc = generateAPIDocFromBackend(absClassFunction, false)
-      if (isSymbol) {
-        val defBody = s"def ${absClassFunction.name}(name : String = null, attr : Map[String, String] = null)(args : org.apache.mxnet.Symbol*)(kwargs : Map[String, Any] = null): org.apache.mxnet.Symbol"
-        s"$scalaDoc\n$defBody"
-      } else {
-        val defBodyWithKwargs = s"def ${absClassFunction.name}(kwargs: Map[String, Any] = null)(args: Any*) : org.apache.mxnet.NDArrayFuncReturn"
-        val defBody = s"def ${absClassFunction.name}(args: Any*) : org.apache.mxnet.NDArrayFuncReturn"
-        s"$scalaDoc\n$defBodyWithKwargs\n$scalaDoc\n$defBody"
+  def nonTypeSafeClassGen(FILE_PATH: String, isSymbol: Boolean): String = {
+    val absFuncs = functionsToGenerate(isSymbol, isContrib = false)
+      .map { func =>
+        val scalaDoc = generateAPIDocFromBackend(func, false)
+        if (isSymbol) {
+          s"""$scalaDoc
+             |def ${func.name}(name : String = null, attr : Map[String, String] = null)
+             |    (args : org.apache.mxnet.Symbol*)(kwargs : Map[String, Any] = null):
+             |    org.apache.mxnet.Symbol
+           """.stripMargin
+        } else {
+          s"""$scalaDoc
+             |def ${func.name}(kwargs: Map[String, Any] = null)
+             |    (args: Any*): org.apache.mxnet.NDArrayFuncReturn
+             |
+             |$scalaDoc
+             |def ${func.name}(args: Any*): org.apache.mxnet.NDArrayFuncReturn
+           """.stripMargin
+        }
       }
-    })
-    val packageName = if (isSymbol) "SymbolBase" else "NDArrayBase"
-    val apacheLicence = "/*\n* Licensed to the Apache Software Foundation (ASF) under one or more\n* contributor license agreements.  See the NOTICE file distributed with\n* this work for additional information regarding copyright ownership.\n* The ASF licenses this file to You under the Apache License, Version 2.0\n* (the \"License\"); you may not use this file except in compliance with\n* the License.  You may obtain a copy of the License at\n*\n*    http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\n"
-    val scalaStyle = "// scalastyle:off"
-    val packageDef = "package org.apache.mxnet"
-    val imports = "import org.apache.mxnet.annotation.Experimental"
-    val absClassDef = s"abstract class $packageName"
-    val finalStr = s"$apacheLicence\n$scalaStyle\n$packageDef\n$imports\n$absClassDef {\n${absFuncs.mkString("\n")}\n}"
-    import java.io._
-    val pw = new PrintWriter(new File(FILE_PATH + s"$packageName.scala"))
-    pw.write(finalStr)
-    pw.close()
-    MD5Generator(finalStr)
+
+    writeFile(
+      FILE_PATH,
+      if (isSymbol) "SymbolBase" else "NDArrayBase",
+      "package org.apache.mxnet",
+      absFuncs)
   }
 
-  // Generate ScalaDoc type
-  def generateAPIDocFromBackend(func : absClassFunction, withParam : Boolean = true) : String = {
-    val desc = ArrayBuffer[String]()
-    desc += "  * <pre>"
-      func.desc.split("\n").foreach({ currStr =>
-      desc += s"  * $currStr"
-    })
-    desc += "  * </pre>"
-    val params = func.listOfArgs.map({ absClassArg =>
-      val currArgName = absClassArg.argName match {
-                case "var" => "vari"
-                case "type" => "typeOf"
-                case _ => absClassArg.argName
+  def javaClassGen(filePath : String) : String = {
+    val notGenerated = Set("Custom")
+    val absClassFunctions = functionsToGenerate(false, false, true)
+    val absFuncs = absClassFunctions.filterNot(ele => notGenerated.contains(ele.name))
+      .groupBy(_.name.toLowerCase).map(ele => {
+      /* Pattern matching for not generating deprecated method
+       * Group all method name in lowercase
+       * Kill the capital lettered method such as Cast vs cast
+       * As it defined by default it deprecated
+       */
+      if (ele._2.length == 1) ele._2.head
+      else {
+        if (ele._2.head.name.head.isLower) ele._2.head
+        else ele._2.last
       }
-      s"  * @param $currArgName\t\t${absClassArg.argDesc}"
-    })
+    }).map(absClassFunction => {
+        generateJavaAPISignature(absClassFunction)
+      }).toSeq
+    val packageName = "NDArrayBase"
+    val packageDef = "package org.apache.mxnet.javaapi"
+    writeFile(filePath + "javaapi/", packageName, packageDef, absFuncs)
+  }
+
+  def generateAPIDocFromBackend(func: Func, withParam: Boolean = true): String = {
+    val desc = func.desc.split("\n")
+      .mkString("  * <pre>\n", "\n  * ", "  * </pre>\n")
+
+    val params = func.listOfArgs.map { absClassArg =>
+      s"  * @param ${absClassArg.safeArgName}\t\t${absClassArg.argDesc}"
+    }
+
     val returnType = s"  * @return ${func.returnType}"
+
     if (withParam) {
-      s"  /**\n${desc.mkString("\n")}\n${params.mkString("\n")}\n$returnType\n  */"
+      s"""  /**
+         |$desc
+         |${params.mkString("\n")}
+         |$returnType
+         |  */""".stripMargin
     } else {
-      s"  /**\n${desc.mkString("\n")}\n$returnType\n  */"
+      s"""  /**
+         |$desc
+         |$returnType
+         |  */""".stripMargin
     }
   }
 
-  def generateAPISignature(func : absClassFunction, isSymbol : Boolean) : String = {
+  def generateAPISignature(func: Func, isSymbol: Boolean): String = {
+    val argDef = ListBuffer[String]()
+
+    argDef ++= typedFunctionCommonArgDef(func)
+
+    if (isSymbol) {
+      argDef += "name : String = null"
+      argDef += "attr : Map[String, String] = null"
+    } else {
+      argDef += "out : Option[NDArray] = None"
+    }
+    val returnType = func.returnType
+
+    s"""@Experimental
+       |def ${func.name} (${argDef.mkString(", ")}): $returnType""".stripMargin
+  }
+
+  def generateJavaAPISignature(func : Func) : String = {
+    val useParamObject = func.listOfArgs.count(arg => arg.isOptional) >= 2
     var argDef = ListBuffer[String]()
+    var classDef = ListBuffer[String]()
+    var requiredParam = ListBuffer[String]()
     func.listOfArgs.foreach(absClassArg => {
-      val currArgName = absClassArg.argName match {
-        case "var" => "vari"
-        case "type" => "typeOf"
-        case _ => absClassArg.argName
-      }
-      if (absClassArg.isOptional) {
-        argDef += s"$currArgName : Option[${absClassArg.argType}] = None"
+      val currArgName = absClassArg.safeArgName
+      // scalastyle:off
+      if (absClassArg.isOptional && useParamObject) {
+        classDef +=
+          s"""private var $currArgName: ${absClassArg.argType} = null
+             |/**
+             | * @param $currArgName\t\t${absClassArg.argDesc}
+             | */
+             |def set${currArgName.capitalize}($currArgName : ${absClassArg.argType}): ${func.name}Param = {
+             |  this.$currArgName = $currArgName
+             |  this
+             | }""".stripMargin
       }
       else {
+        requiredParam += s"  * @param $currArgName\t\t${absClassArg.argDesc}"
         argDef += s"$currArgName : ${absClassArg.argType}"
       }
+      classDef += s"def get${currArgName.capitalize}() = this.$currArgName"
+      // scalastyle:on
     })
-    var returnType = func.returnType
-    if (isSymbol) {
-      argDef += "name : String = null"
-      argDef += "attr : Map[String, String] = null"
+    val experimentalTag = "@Experimental"
+    val returnType = "Array[NDArray]"
+    val scalaDoc = generateAPIDocFromBackend(func)
+    val scalaDocNoParam = generateAPIDocFromBackend(func, false)
+    if(useParamObject) {
+      classDef +=
+        s"""private var out : org.apache.mxnet.NDArray = null
+           |def setOut(out : NDArray) : ${func.name}Param = {
+           |  this.out = out
+           |  this
+           | }
+           | def getOut() = this.out
+           | """.stripMargin
+      s"""$scalaDocNoParam
+         | $experimentalTag
+         | def ${func.name}(po: ${func.name}Param) : $returnType
+         | /**
+         | * This Param Object is specifically used for ${func.name}
+         | ${requiredParam.mkString("\n")}
+         | */
+         | class ${func.name}Param(${argDef.mkString(",")}) {
+         |  ${classDef.mkString("\n  ")}
+         | }""".stripMargin
     } else {
-      argDef += "out : Option[NDArray] = None"
-      returnType = "org.apache.mxnet.NDArrayFuncReturn"
+      argDef += "out : NDArray"
+      s"""$scalaDoc
+         |$experimentalTag
+         | def ${func.name}(${argDef.mkString(", ")}) : $returnType
+         | """.stripMargin
     }
-    val experimentalTag = "@Experimental"
-    s"$experimentalTag\ndef ${func.name} (${argDef.mkString(", ")}) : $returnType"
   }
 
+  def writeFile(FILE_PATH: String, className: String, packageDef: String,
+                absFuncs: Seq[String]): String = {
 
-  // List and add all the atomic symbol functions to current module.
-  private def getSymbolNDArrayMethods(isSymbol : Boolean): List[absClassFunction] = {
-    val opNames = ListBuffer.empty[String]
-    val returnType = if (isSymbol) "Symbol" else "NDArray"
-    _LIB.mxListAllOpNames(opNames)
-    // TODO: Add '_linalg_', '_sparse_', '_image_' support
-    // TODO: Add Filter to the same location in case of refactor
-    opNames.map(opName => {
-      val opHandle = new RefLong
-      _LIB.nnGetOpHandle(opName, opHandle)
-      makeAtomicSymbolFunction(opHandle.value, opName, "org.apache.mxnet." + returnType)
-    }).toList.filterNot(_.name.startsWith("_"))
-  }
-
-  // Create an atomic symbol function by handle and function name.
-  private def makeAtomicSymbolFunction(handle: SymbolHandle, aliasName: String, returnType : String)
-  : absClassFunction = {
-    val name = new RefString
-    val desc = new RefString
-    val keyVarNumArgs = new RefString
-    val numArgs = new RefInt
-    val argNames = ListBuffer.empty[String]
-    val argTypes = ListBuffer.empty[String]
-    val argDescs = ListBuffer.empty[String]
-
-    _LIB.mxSymbolGetAtomicSymbolInfo(
-      handle, name, desc, numArgs, argNames, argTypes, argDescs, keyVarNumArgs)
-    val argList = argNames zip argTypes zip argDescs map { case ((argName, argType), argDesc) =>
-      val typeAndOption = CToScalaUtils.argumentCleaner(argName, argType, returnType)
-      new absClassArg(argName, typeAndOption._1, argDesc, typeAndOption._2)
-    }
-    new absClassFunction(aliasName, desc.value, argList.toList, returnType)
+    val finalStr =
+      s"""/*
+         |* Licensed to the Apache Software Foundation (ASF) under one or more
+         |* contributor license agreements.  See the NOTICE file distributed with
+         |* this work for additional information regarding copyright ownership.
+         |* The ASF licenses this file to You under the Apache License, Version 2.0
+         |* (the "License"); you may not use this file except in compliance with
+         |* the License.  You may obtain a copy of the License at
+         |*
+         |*    http://www.apache.org/licenses/LICENSE-2.0
+         |*
+         |* Unless required by applicable law or agreed to in writing, software
+         |* distributed under the License is distributed on an "AS IS" BASIS,
+         |* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+         |* See the License for the specific language governing permissions and
+         |* limitations under the License.
+         |*/
+         |
+         |$packageDef
+         |
+         |import org.apache.mxnet.annotation.Experimental
+         |
+         |// scalastyle:off
+         |abstract class $className {
+         |${absFuncs.mkString("\n")}
+         |}""".stripMargin
+    val pw = new PrintWriter(new File(FILE_PATH + s"$className.scala"))
+    pw.write(finalStr)
+    pw.close()
+    MD5Generator(finalStr)
   }
 }
diff --git a/scala-package/macros/src/main/scala/org/apache/mxnet/GeneratorBase.scala b/scala-package/macros/src/main/scala/org/apache/mxnet/GeneratorBase.scala
new file mode 100644
index 00000000000..095d05e838d
--- /dev/null
+++ b/scala-package/macros/src/main/scala/org/apache/mxnet/GeneratorBase.scala
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet
+
+import org.apache.mxnet.init.Base.{RefInt, RefLong, RefString, _LIB}
+import org.apache.mxnet.utils.{CToScalaUtils, OperatorBuildUtils}
+
+import scala.collection.mutable.ListBuffer
+import scala.reflect.macros.blackbox
+
+abstract class GeneratorBase {
+  type Handle = Long
+
+  case class Arg(argName: String, argType: String, argDesc: String, isOptional: Boolean) {
+    def safeArgName: String = argName match {
+      case "var" => "vari"
+      case "type" => "typeOf"
+      case _ => argName
+    }
+  }
+
+  case class Func(name: String, desc: String, listOfArgs: List[Arg], returnType: String)
+
+  def functionsToGenerate(isSymbol: Boolean, isContrib: Boolean,
+                          isJava: Boolean = false): List[Func] = {
+    val l = getBackEndFunctions(isSymbol, isJava)
+    if (isContrib) {
+      l.filter(func => func.name.startsWith("_contrib_") || !func.name.startsWith("_"))
+    } else {
+      l.filterNot(_.name.startsWith("_"))
+    }
+  }
+
+  def typeSafeFunctionsToGenerate(isSymbol: Boolean, isContrib: Boolean): List[Func] = {
+    // Operators that should not be generated
+    val notGenerated = Set("Custom")
+
+    val l = getBackEndFunctions(isSymbol)
+    val res = if (isContrib) {
+      l.filter(func => func.name.startsWith("_contrib_") || !func.name.startsWith("_"))
+    } else {
+      l.filterNot(_.name.startsWith("_"))
+    }
+    res.filterNot(ele => notGenerated.contains(ele.name))
+  }
+
+  protected def getBackEndFunctions(isSymbol: Boolean, isJava: Boolean = false): List[Func] = {
+    val opNames = ListBuffer.empty[String]
+    _LIB.mxListAllOpNames(opNames)
+    opNames.map(opName => {
+      val opHandle = new RefLong
+      _LIB.nnGetOpHandle(opName, opHandle)
+      makeAtomicFunction(opHandle.value, opName, isSymbol, isJava)
+    }).toList
+  }
+
+  private def makeAtomicFunction(handle: Handle, aliasName: String,
+                                 isSymbol: Boolean, isJava: Boolean): Func = {
+    val name = new RefString
+    val desc = new RefString
+    val keyVarNumArgs = new RefString
+    val numArgs = new RefInt
+    val argNames = ListBuffer.empty[String]
+    val argTypes = ListBuffer.empty[String]
+    val argDescs = ListBuffer.empty[String]
+
+    _LIB.mxSymbolGetAtomicSymbolInfo(
+      handle, name, desc, numArgs, argNames, argTypes, argDescs, keyVarNumArgs)
+    val paramStr = OperatorBuildUtils.ctypes2docstring(argNames, argTypes, argDescs)
+    val extraDoc: String = if (keyVarNumArgs.value != null && keyVarNumArgs.value.length > 0) {
+      s"This function support variable length of positional input (${keyVarNumArgs.value})."
+    } else {
+      ""
+    }
+    val realName = if (aliasName == name.value) "" else s"(a.k.a., ${name.value})"
+    val docStr = s"$aliasName $realName\n${desc.value}\n\n$paramStr\n$extraDoc\n"
+
+    val argList = argNames zip argTypes zip argDescs map { case ((argName, argType), argDesc) =>
+      val family = if (isJava) "org.apache.mxnet.javaapi.NDArray"
+      else if (isSymbol) "org.apache.mxnet.Symbol"
+      else "org.apache.mxnet.NDArray"
+      val typeAndOption =
+        CToScalaUtils.argumentCleaner(argName, argType, family)
+      Arg(argName, typeAndOption._1, argDesc, typeAndOption._2)
+    }
+    val returnType =
+      if (isJava) "Array[org.apache.mxnet.javaapi.NDArray]"
+      else if (isSymbol) "org.apache.mxnet.Symbol"
+      else "org.apache.mxnet.NDArrayFuncReturn"
+    Func(aliasName, desc.value, argList.toList, returnType)
+  }
+
+  /**
+    * Generate class structure for all function APIs
+    *
+    * @param c
+    * @param funcDef DefDef type of function definitions
+    * @param annottees
+    * @return
+    */
+  protected def structGeneration(c: blackbox.Context)
+                                (funcDef: List[c.universe.DefDef], annottees: c.Expr[Any]*)
+  : c.Expr[Any] = {
+    import c.universe._
+    val inputs = annottees.map(_.tree).toList
+    // pattern match on the inputs
+    val modDefs = inputs map {
+      case ClassDef(mods, name, something, template) =>
+        val q = template match {
+          case Template(superMaybe, emptyValDef, defs) =>
+            Template(superMaybe, emptyValDef, defs ++ funcDef)
+          case ex =>
+            throw new IllegalArgumentException(s"Invalid template: $ex")
+        }
+        ClassDef(mods, name, something, q)
+      case ModuleDef(mods, name, template) =>
+        val q = template match {
+          case Template(superMaybe, emptyValDef, defs) =>
+            Template(superMaybe, emptyValDef, defs ++ funcDef)
+          case ex =>
+            throw new IllegalArgumentException(s"Invalid template: $ex")
+        }
+        ModuleDef(mods, name, q)
+      case ex =>
+        throw new IllegalArgumentException(s"Invalid macro input: $ex")
+    }
+    // wrap the result up in an Expr, and return it
+    val result = c.Expr(Block(modDefs, Literal(Constant())))
+    result
+  }
+
+  protected def typedFunctionCommonArgDef(func: Func): List[String] = {
+    // build function argument definition, with optionality, and safe names
+    func.listOfArgs.map(arg =>
+      if (arg.isOptional) {
+        // let's avoid a stupid Option[Array[...]]
+        if (arg.argType.startsWith("Array[")) {
+          s"${arg.safeArgName} : ${arg.argType} = Array.empty"
+        } else {
+          s"${arg.safeArgName} : Option[${arg.argType}] = None"
+        }
+      }
+      else {
+        s"${arg.safeArgName} : ${arg.argType}"
+      }
+    )
+  }
+
+}
diff --git a/scala-package/macros/src/main/scala/org/apache/mxnet/NDArrayMacro.scala b/scala-package/macros/src/main/scala/org/apache/mxnet/NDArrayMacro.scala
index 2d3a1c7ec5a..d85abe1ecc4 100644
--- a/scala-package/macros/src/main/scala/org/apache/mxnet/NDArrayMacro.scala
+++ b/scala-package/macros/src/main/scala/org/apache/mxnet/NDArrayMacro.scala
@@ -17,11 +17,8 @@
 
 package org.apache.mxnet
 
-import org.apache.mxnet.init.Base._
-import org.apache.mxnet.utils.{CToScalaUtils, OperatorBuildUtils}
-
 import scala.annotation.StaticAnnotation
-import scala.collection.mutable.{ArrayBuffer, ListBuffer}
+import scala.collection.mutable.ListBuffer
 import scala.language.experimental.macros
 import scala.reflect.macros.blackbox
 
@@ -30,207 +27,111 @@ private[mxnet] class AddNDArrayFunctions(isContrib: Boolean) extends StaticAnnot
 }
 
 private[mxnet] class AddNDArrayAPIs(isContrib: Boolean) extends StaticAnnotation {
-  private[mxnet] def macroTransform(annottees: Any*) = macro NDArrayMacro.typeSafeAPIDefs
+  private[mxnet] def macroTransform(annottees: Any*) = macro TypedNDArrayAPIMacro.typeSafeAPIDefs
 }
 
-private[mxnet] object NDArrayMacro {
-  case class NDArrayArg(argName: String, argType: String, isOptional : Boolean)
-  case class NDArrayFunction(name: String, listOfArgs: List[NDArrayArg])
-
-  // scalastyle:off havetype
-  def addDefs(c: blackbox.Context)(annottees: c.Expr[Any]*) = {
-    impl(c)(annottees: _*)
-  }
-  def typeSafeAPIDefs(c: blackbox.Context)(annottees: c.Expr[Any]*) = {
-    typeSafeAPIImpl(c)(annottees: _*)
-  }
-  // scalastyle:off havetype
-
-  private val ndarrayFunctions: List[NDArrayFunction] = initNDArrayModule()
+private[mxnet] object NDArrayMacro extends GeneratorBase {
 
-  private def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
+  def addDefs(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
     import c.universe._
-
     val isContrib: Boolean = c.prefix.tree match {
       case q"new AddNDArrayFunctions($b)" => c.eval[Boolean](c.Expr(b))
     }
 
-    val newNDArrayFunctions = {
-      if (isContrib) ndarrayFunctions.filter(_.name.startsWith("_contrib_"))
-      else ndarrayFunctions.filterNot(_.name.startsWith("_"))
-    }
-
-     val functionDefs = newNDArrayFunctions flatMap { NDArrayfunction =>
-        val funcName = NDArrayfunction.name
-        val termName = TermName(funcName)
-       Seq(
-            // scalastyle:off
-            // (yizhi) We are investigating a way to make these functions type-safe
-            // and waiting to see the new approach is stable enough.
-            // Thus these functions may be deprecated in the future.
-            // e.g def transpose(kwargs: Map[String, Any] = null)(args: Any*)
-            q"def $termName(kwargs: Map[String, Any] = null)(args: Any*) = {genericNDArrayFunctionInvoke($funcName, args, kwargs)}".asInstanceOf[DefDef],
-            // e.g def transpose(args: Any*)
-            q"def $termName(args: Any*) = {genericNDArrayFunctionInvoke($funcName, args, null)}".asInstanceOf[DefDef]
-            // scalastyle:on
-          )
-        }
-
-    structGeneration(c)(functionDefs, annottees : _*)
+    impl(c)(isContrib, annottees: _*)
   }
 
-  private def typeSafeAPIImpl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
+  private def impl(c: blackbox.Context)
+                  (isContrib: Boolean, annottees: c.Expr[Any]*): c.Expr[Any] = {
     import c.universe._
 
-    val isContrib: Boolean = c.prefix.tree match {
-      case q"new AddNDArrayAPIs($b)" => c.eval[Boolean](c.Expr(b))
-    }
-    // Defines Operators that should not generated
-    val notGenerated = Set("Custom")
-
-    val newNDArrayFunctions = {
-      if (isContrib) ndarrayFunctions.filter(
-        func => func.name.startsWith("_contrib_") || !func.name.startsWith("_"))
-      else ndarrayFunctions.filterNot(_.name.startsWith("_"))
-    }.filterNot(ele => notGenerated.contains(ele.name))
-
-    val functionDefs = newNDArrayFunctions.map { ndarrayfunction =>
-
-      // Construct argument field
-      var argDef = ListBuffer[String]()
-      // Construct Implementation field
-      var impl = ListBuffer[String]()
-      impl += "val map = scala.collection.mutable.Map[String, Any]()"
-      impl += "val args = scala.collection.mutable.ArrayBuffer.empty[NDArray]"
-      ndarrayfunction.listOfArgs.foreach({ ndarrayarg =>
-        // var is a special word used to define variable in Scala,
-        // need to changed to something else in order to make it work
-        val currArgName = ndarrayarg.argName match {
-          case "var" => "vari"
-          case "type" => "typeOf"
-          case default => ndarrayarg.argName
-        }
-        if (ndarrayarg.isOptional) {
-          argDef += s"${currArgName} : Option[${ndarrayarg.argType}] = None"
-        }
-        else {
-          argDef += s"${currArgName} : ${ndarrayarg.argType}"
-        }
-        // NDArray arg implementation
-        val returnType = "org.apache.mxnet.NDArray"
-
-        // TODO: Currently we do not add place holder for NDArray
-        // Example: an NDArray operator like the following format
-        // nd.foo(arg1: NDArray(required), arg2: NDArray(Optional), arg3: NDArray(Optional)
-        // If we place nd.foo(arg1, arg3 = arg3), do we need to add place holder for arg2?
-        // What it should be?
-        val base =
-          if (ndarrayarg.argType.equals(returnType)) {
-            s"args += $currArgName"
-          } else if (ndarrayarg.argType.equals(s"Array[$returnType]")){
-            s"args ++= $currArgName"
-          } else {
-            "map(\"" + ndarrayarg.argName + "\") = " + currArgName
-          }
-        impl.append(
-          if (ndarrayarg.isOptional) s"if (!$currArgName.isEmpty) $base.get"
-          else base
-        )
-      })
-      // add default out parameter
-      argDef += "out : Option[NDArray] = None"
-      impl += "if (!out.isEmpty) map(\"out\") = out.get"
-      // scalastyle:off
-      impl += "org.apache.mxnet.NDArray.genericNDArrayFunctionInvoke(\"" + ndarrayfunction.name + "\", args.toSeq, map.toMap)"
-      // scalastyle:on
-      // Combine and build the function string
-      val returnType = "org.apache.mxnet.NDArrayFuncReturn"
-      var finalStr = s"def ${ndarrayfunction.name}"
-      finalStr += s" (${argDef.mkString(",")}) : $returnType"
-      finalStr += s" = {${impl.mkString("\n")}}"
-      c.parse(finalStr).asInstanceOf[DefDef]
+    val functions = functionsToGenerate(isSymbol = false, isContrib)
+
+    val functionDefs = functions.flatMap { NDArrayfunction =>
+      val funcName = NDArrayfunction.name
+      val termName = TermName(funcName)
+      Seq(
+        // e.g def transpose(kwargs: Map[String, Any] = null)(args: Any*)
+        q"""
+             def $termName(kwargs: Map[String, Any] = null)(args: Any*) = {
+               genericNDArrayFunctionInvoke($funcName, args, kwargs)
+             }
+          """.asInstanceOf[DefDef],
+        // e.g def transpose(args: Any*)
+        q"""
+             def $termName(args: Any*) = {
+               genericNDArrayFunctionInvoke($funcName, args, null)
+             }
+          """.asInstanceOf[DefDef]
+      )
     }
 
-    structGeneration(c)(functionDefs, annottees : _*)
+    structGeneration(c)(functionDefs, annottees: _*)
   }
+}
 
-  private def structGeneration(c: blackbox.Context)
-                              (funcDef : List[c.universe.DefDef], annottees: c.Expr[Any]*)
-  : c.Expr[Any] = {
+private[mxnet] object TypedNDArrayAPIMacro extends GeneratorBase {
+
+  def typeSafeAPIDefs(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
     import c.universe._
-    val inputs = annottees.map(_.tree).toList
-    // pattern match on the inputs
-    val modDefs = inputs map {
-      case ClassDef(mods, name, something, template) =>
-        val q = template match {
-          case Template(superMaybe, emptyValDef, defs) =>
-            Template(superMaybe, emptyValDef, defs ++ funcDef)
-          case ex =>
-            throw new IllegalArgumentException(s"Invalid template: $ex")
-        }
-        ClassDef(mods, name, something, q)
-      case ModuleDef(mods, name, template) =>
-        val q = template match {
-          case Template(superMaybe, emptyValDef, defs) =>
-            Template(superMaybe, emptyValDef, defs ++ funcDef)
-          case ex =>
-            throw new IllegalArgumentException(s"Invalid template: $ex")
-        }
-        ModuleDef(mods, name, q)
-      case ex =>
-        throw new IllegalArgumentException(s"Invalid macro input: $ex")
+    val isContrib: Boolean = c.prefix.tree match {
+      case q"new AddNDArrayAPIs($b)" => c.eval[Boolean](c.Expr(b))
     }
-    // wrap the result up in an Expr, and return it
-    val result = c.Expr(Block(modDefs, Literal(Constant())))
-    result
+
+    val functions = typeSafeFunctionsToGenerate(isSymbol = false, isContrib)
+
+    val functionDefs = functions.map(f => buildTypedFunction(c)(f))
+    structGeneration(c)(functionDefs, annottees: _*)
   }
 
+  protected def buildTypedFunction(c: blackbox.Context)
+                                  (function: Func): c.universe.DefDef = {
+    import c.universe._
 
+    val returnType = "org.apache.mxnet.NDArrayFuncReturn"
+    val ndarrayType = "org.apache.mxnet.NDArray"
 
+    // Construct argument field
+    val argDef = ListBuffer[String]()
+    argDef ++= typedFunctionCommonArgDef(function)
+    argDef += "out : Option[NDArray] = None"
 
-  // List and add all the atomic symbol functions to current module.
-  private def initNDArrayModule(): List[NDArrayFunction] = {
-    val opNames = ListBuffer.empty[String]
-    _LIB.mxListAllOpNames(opNames)
-    opNames.map(opName => {
-      val opHandle = new RefLong
-      _LIB.nnGetOpHandle(opName, opHandle)
-      makeNDArrayFunction(opHandle.value, opName)
-    }).toList
-  }
+    // Construct Implementation field
+    var impl = ListBuffer[String]()
+    impl += "val map = scala.collection.mutable.Map[String, Any]()"
+    impl += s"val args = scala.collection.mutable.ArrayBuffer.empty[$ndarrayType]"
 
-  // Create an atomic symbol function by handle and function name.
-  private def makeNDArrayFunction(handle: NDArrayHandle, aliasName: String)
-  : NDArrayFunction = {
-    val name = new RefString
-    val desc = new RefString
-    val keyVarNumArgs = new RefString
-    val numArgs = new RefInt
-    val argNames = ListBuffer.empty[String]
-    val argTypes = ListBuffer.empty[String]
-    val argDescs = ListBuffer.empty[String]
-
-    _LIB.mxSymbolGetAtomicSymbolInfo(
-      handle, name, desc, numArgs, argNames, argTypes, argDescs, keyVarNumArgs)
-    val paramStr = OperatorBuildUtils.ctypes2docstring(argNames, argTypes, argDescs)
-    val extraDoc: String = if (keyVarNumArgs.value != null && keyVarNumArgs.value.length > 0) {
-      s"This function support variable length of positional input (${keyVarNumArgs.value})."
-    } else {
-      ""
-    }
-    val realName = if (aliasName == name.value) "" else s"(a.k.a., ${name.value})"
-    val docStr = s"$aliasName $realName\n${desc.value}\n\n$paramStr\n$extraDoc\n"
-    // scalastyle:off println
-    if (System.getenv("MXNET4J_PRINT_OP_DEF") != null
-      && System.getenv("MXNET4J_PRINT_OP_DEF").toLowerCase == "true") {
-      println("NDArray function definition:\n" + docStr)
-    }
-    // scalastyle:on println
-    val argList = argNames zip argTypes map { case (argName, argType) =>
-      val typeAndOption =
-        CToScalaUtils.argumentCleaner(argName, argType, "org.apache.mxnet.NDArray")
-      new NDArrayArg(argName, typeAndOption._1, typeAndOption._2)
+    // NDArray arg implementation
+    impl ++= function.listOfArgs.map { arg =>
+      if (arg.argType.equals(s"Array[$ndarrayType]")) {
+        s"args ++= ${arg.safeArgName}"
+      } else {
+        val base =
+          if (arg.argType.equals(ndarrayType)) {
+            // ndarrays go to args
+            s"args += ${arg.safeArgName}"
+          } else {
+            // other types go to kwargs
+            s"""map("${arg.argName}") = ${arg.safeArgName}"""
+          }
+        if (arg.isOptional) s"if (!${arg.safeArgName}.isEmpty) $base.get"
+        else base
+      }
     }
-    new NDArrayFunction(aliasName, argList.toList)
+
+    impl +=
+      s"""if (!out.isEmpty) map("out") = out.get
+         |org.apache.mxnet.NDArray.genericNDArrayFunctionInvoke(
+         |  "${function.name}", args.toSeq, map.toMap)
+       """.stripMargin
+
+    // Combine and build the function string
+    val finalStr =
+      s"""def ${function.name}
+         |   (${argDef.mkString(",")}) : $returnType
+         | = {${impl.mkString("\n")}}
+       """.stripMargin
+
+    c.parse(finalStr).asInstanceOf[DefDef]
   }
 }
diff --git a/scala-package/macros/src/main/scala/org/apache/mxnet/SymbolMacro.scala b/scala-package/macros/src/main/scala/org/apache/mxnet/SymbolMacro.scala
index 42aa11781d8..ab864e1ef19 100644
--- a/scala-package/macros/src/main/scala/org/apache/mxnet/SymbolMacro.scala
+++ b/scala-package/macros/src/main/scala/org/apache/mxnet/SymbolMacro.scala
@@ -21,222 +21,106 @@ import scala.annotation.StaticAnnotation
 import scala.collection.mutable.ListBuffer
 import scala.language.experimental.macros
 import scala.reflect.macros.blackbox
-import org.apache.mxnet.init.Base._
-import org.apache.mxnet.utils.{CToScalaUtils, OperatorBuildUtils}
 
 private[mxnet] class AddSymbolFunctions(isContrib: Boolean) extends StaticAnnotation {
-  private[mxnet] def macroTransform(annottees: Any*) = macro SymbolImplMacros.addDefs
+  private[mxnet] def macroTransform(annottees: Any*) = macro SymbolMacro.addDefs
 }
 
 private[mxnet] class AddSymbolAPIs(isContrib: Boolean) extends StaticAnnotation {
-  private[mxnet] def macroTransform(annottees: Any*) = macro SymbolImplMacros.typeSafeAPIDefs
+  private[mxnet] def macroTransform(annottees: Any*) = macro TypedSymbolAPIMacro.typeSafeAPIDefs
 }
 
-private[mxnet] object SymbolImplMacros {
-  case class SymbolArg(argName: String, argType: String, isOptional : Boolean)
-  case class SymbolFunction(name: String, listOfArgs: List[SymbolArg])
+private[mxnet] object SymbolMacro extends GeneratorBase {
 
-  // scalastyle:off havetype
-  def addDefs(c: blackbox.Context)(annottees: c.Expr[Any]*) = {
-    impl(c)(annottees: _*)
-  }
-  def typeSafeAPIDefs(c: blackbox.Context)(annottees: c.Expr[Any]*) = {
-    typedAPIImpl(c)(annottees: _*)
-  }
-  // scalastyle:on havetype
-
-  private val symbolFunctions: List[SymbolFunction] = initSymbolModule()
-
-  /**
-    * Implementation for fixed input API structure
-    */
-  private def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
+  def addDefs(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
     import c.universe._
-
     val isContrib: Boolean = c.prefix.tree match {
       case q"new AddSymbolFunctions($b)" => c.eval[Boolean](c.Expr(b))
     }
 
-    val newSymbolFunctions = {
-      if (isContrib) symbolFunctions.filter(
-        func => func.name.startsWith("_contrib_") || !func.name.startsWith("_"))
-      else symbolFunctions.filter(!_.name.startsWith("_"))
-    }
+    impl(c)(isContrib, annottees: _*)
+  }
+
+  private def impl(c: blackbox.Context)
+                  (isContrib: Boolean, annottees: c.Expr[Any]*): c.Expr[Any] = {
+    import c.universe._
 
+    val functions = functionsToGenerate(isSymbol = false, isContrib)
 
-    val functionDefs = newSymbolFunctions map { symbolfunction =>
-        val funcName = symbolfunction.name
-        val tName = TermName(funcName)
-        q"""
+    val functionDefs = functions.map { symbolfunction =>
+      val funcName = symbolfunction.name
+      val tName = TermName(funcName)
+      q"""
             def $tName(name : String = null, attr : Map[String, String] = null)
-            (args : org.apache.mxnet.Symbol*)(kwargs : Map[String, Any] = null)
-             : org.apache.mxnet.Symbol = {
-              createSymbolGeneral($funcName,name,attr,args,kwargs)
-              }
+              (args : org.apache.mxnet.Symbol*)(kwargs : Map[String, Any] = null)
+              : org.apache.mxnet.Symbol = {
+                createSymbolGeneral($funcName,name,attr,args,kwargs)
+            }
          """.asInstanceOf[DefDef]
-      }
+    }
 
-    structGeneration(c)(functionDefs, annottees : _*)
+    structGeneration(c)(functionDefs, annottees: _*)
   }
+}
 
-  /**
-    * Implementation for Dynamic typed API Symbol.api.<functioname>
-    */
-  private def typedAPIImpl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
-    import c.universe._
+private[mxnet] object TypedSymbolAPIMacro extends GeneratorBase {
 
+  def typeSafeAPIDefs(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
+    import c.universe._
     val isContrib: Boolean = c.prefix.tree match {
       case q"new AddSymbolAPIs($b)" => c.eval[Boolean](c.Expr(b))
     }
 
-    // Defines Operators that should not generated
-    val notGenerated = Set("Custom")
-
-    // TODO: Put Symbol.api.foo --> Stable APIs
-    // Symbol.contrib.bar--> Contrib APIs
-    val newSymbolFunctions = {
-      if (isContrib) symbolFunctions.filter(
-        func => func.name.startsWith("_contrib_") || !func.name.startsWith("_"))
-      else symbolFunctions.filter(!_.name.startsWith("_"))
-    }.filterNot(ele => notGenerated.contains(ele.name))
-
-    val functionDefs = newSymbolFunctions map { symbolfunction =>
-
-      // Construct argument field
-      var argDef = ListBuffer[String]()
-      // Construct Implementation field
-      var impl = ListBuffer[String]()
-      impl += "val map = scala.collection.mutable.Map[String, Any]()"
-      impl += "var args = Seq[org.apache.mxnet.Symbol]()"
-      symbolfunction.listOfArgs.foreach({ symbolarg =>
-        // var is a special word used to define variable in Scala,
-        // need to changed to something else in order to make it work
-        val currArgName = symbolarg.argName match {
-          case "var" => "vari"
-          case "type" => "typeOf"
-          case default => symbolarg.argName
-        }
-        if (symbolarg.isOptional) {
-          argDef += s"${currArgName} : Option[${symbolarg.argType}] = None"
-        }
-        else {
-          argDef += s"${currArgName} : ${symbolarg.argType}"
-        }
-        // Symbol arg implementation
-        val returnType = "org.apache.mxnet.Symbol"
-        val base =
-        if (symbolarg.argType.equals(s"Array[$returnType]")) {
-          if (symbolarg.isOptional) s"if (!$currArgName.isEmpty) args = $currArgName.get.toSeq"
-          else s"args = $currArgName.toSeq"
-        } else {
-          if (symbolarg.isOptional) {
-            // scalastyle:off
-            s"if (!$currArgName.isEmpty) map(" + "\"" + symbolarg.argName + "\"" + s") = $currArgName.get"
-            // scalastyle:on
-          }
-          else "map(\"" + symbolarg.argName + "\"" + s") = $currArgName"
-        }
+    val functions = typeSafeFunctionsToGenerate(isSymbol = true, isContrib)
 
-        impl += base
-      })
-      argDef += "name : String = null"
-      argDef += "attr : Map[String, String] = null"
-      // scalastyle:off
-      // TODO: Seq() here allows user to place Symbols rather than normal arguments to run, need to fix if old API deprecated
-      impl += "org.apache.mxnet.Symbol.createSymbolGeneral(\"" + symbolfunction.name + "\", name, attr, args, map.toMap)"
-      // scalastyle:on
-      // Combine and build the function string
-      val returnType = "org.apache.mxnet.Symbol"
-      var finalStr = s"def ${symbolfunction.name}"
-      finalStr += s" (${argDef.mkString(",")}) : $returnType"
-      finalStr += s" = {${impl.mkString("\n")}}"
-      c.parse(finalStr).asInstanceOf[DefDef]
-    }
-    structGeneration(c)(functionDefs, annottees : _*)
+    val functionDefs = functions.map(f => buildTypedFunction(c)(f))
+    structGeneration(c)(functionDefs, annottees: _*)
   }
 
-  /**
-    * Generate class structure for all function APIs
-    * @param c
-    * @param funcDef DefDef type of function definitions
-    * @param annottees
-    * @return
-    */
-  private def structGeneration(c: blackbox.Context)
-                              (funcDef : List[c.universe.DefDef], annottees: c.Expr[Any]*)
-  : c.Expr[Any] = {
+  protected def buildTypedFunction(c: blackbox.Context)
+                                  (function: Func): c.universe.DefDef = {
     import c.universe._
-    val inputs = annottees.map(_.tree).toList
-    // pattern match on the inputs
-    val modDefs = inputs map {
-      case ClassDef(mods, name, something, template) =>
-        val q = template match {
-          case Template(superMaybe, emptyValDef, defs) =>
-            Template(superMaybe, emptyValDef, defs ++ funcDef)
-          case ex =>
-            throw new IllegalArgumentException(s"Invalid template: $ex")
-        }
-        ClassDef(mods, name, something, q)
-      case ModuleDef(mods, name, template) =>
-        val q = template match {
-          case Template(superMaybe, emptyValDef, defs) =>
-            Template(superMaybe, emptyValDef, defs ++ funcDef)
-          case ex =>
-            throw new IllegalArgumentException(s"Invalid template: $ex")
-        }
-        ModuleDef(mods, name, q)
-      case ex =>
-        throw new IllegalArgumentException(s"Invalid macro input: $ex")
-    }
-    // wrap the result up in an Expr, and return it
-    val result = c.Expr(Block(modDefs, Literal(Constant())))
-    result
-  }
 
-  // List and add all the atomic symbol functions to current module.
-  private def initSymbolModule(): List[SymbolFunction] = {
-    val opNames = ListBuffer.empty[String]
-    _LIB.mxListAllOpNames(opNames)
-    // TODO: Add '_linalg_', '_sparse_', '_image_' support
-    opNames.map(opName => {
-      val opHandle = new RefLong
-      _LIB.nnGetOpHandle(opName, opHandle)
-      makeAtomicSymbolFunction(opHandle.value, opName)
-    }).toList
-  }
+    val returnType = "org.apache.mxnet.Symbol"
+    val symbolType = "org.apache.mxnet.Symbol"
 
-  // Create an atomic symbol function by handle and function name.
-  private def makeAtomicSymbolFunction(handle: SymbolHandle, aliasName: String)
-      : SymbolFunction = {
-    val name = new RefString
-    val desc = new RefString
-    val keyVarNumArgs = new RefString
-    val numArgs = new RefInt
-    val argNames = ListBuffer.empty[String]
-    val argTypes = ListBuffer.empty[String]
-    val argDescs = ListBuffer.empty[String]
-
-    _LIB.mxSymbolGetAtomicSymbolInfo(
-      handle, name, desc, numArgs, argNames, argTypes, argDescs, keyVarNumArgs)
-    val paramStr = OperatorBuildUtils.ctypes2docstring(argNames, argTypes, argDescs)
-    val extraDoc: String = if (keyVarNumArgs.value != null && keyVarNumArgs.value.length > 0) {
-        s"This function support variable length of positional input (${keyVarNumArgs.value})."
+    // Construct argument field
+    val argDef = ListBuffer[String]()
+    argDef ++= typedFunctionCommonArgDef(function)
+    argDef += "name : String = null"
+    argDef += "attr : Map[String, String] = null"
+
+    // Construct Implementation field
+    val impl = ListBuffer[String]()
+    impl += "val map = scala.collection.mutable.Map[String, Any]()"
+    impl += s"var args = scala.collection.Seq[$symbolType]()"
+
+    // Symbol arg implementation
+    impl ++= function.listOfArgs.map { arg =>
+      if (arg.argType.equals(s"Array[$symbolType]")) {
+        s"if (!${arg.safeArgName}.isEmpty) args = ${arg.safeArgName}.toSeq"
       } else {
-        ""
+        // all go in kwargs
+        if (arg.isOptional) {
+          s"""if (!${arg.safeArgName}.isEmpty) map("${arg.argName}") = ${arg.safeArgName}.get"""
+        } else {
+          s"""map("${arg.argName}") = ${arg.safeArgName}"""
+        }
       }
-    val realName = if (aliasName == name.value) "" else s"(a.k.a., ${name.value})"
-    val docStr = s"$aliasName $realName\n${desc.value}\n\n$paramStr\n$extraDoc\n"
-    // scalastyle:off println
-    if (System.getenv("MXNET4J_PRINT_OP_DEF") != null
-          && System.getenv("MXNET4J_PRINT_OP_DEF").toLowerCase == "true") {
-      println("Symbol function definition:\n" + docStr)
     }
-    // scalastyle:on println
-    val argList = argNames zip argTypes map { case (argName, argType) =>
-        val typeAndOption =
-          CToScalaUtils.argumentCleaner(argName, argType, "org.apache.mxnet.Symbol")
-        new SymbolArg(argName, typeAndOption._1, typeAndOption._2)
-    }
-    new SymbolFunction(aliasName, argList.toList)
+
+    impl +=
+      s"""org.apache.mxnet.Symbol.createSymbolGeneral(
+         |  "${function.name}", name, attr, args, map.toMap)
+       """.stripMargin
+
+    // Combine and build the function string
+    val finalStr =
+      s"""def ${function.name}
+         |   (${argDef.mkString(",")}) : $returnType
+         | = {${impl.mkString("\n")}}
+       """.stripMargin
+
+    c.parse(finalStr).asInstanceOf[DefDef]
   }
 }
diff --git a/scala-package/macros/src/main/scala/org/apache/mxnet/javaapi/JavaNDArrayMacro.scala b/scala-package/macros/src/main/scala/org/apache/mxnet/javaapi/JavaNDArrayMacro.scala
new file mode 100644
index 00000000000..4dfd6eb044a
--- /dev/null
+++ b/scala-package/macros/src/main/scala/org/apache/mxnet/javaapi/JavaNDArrayMacro.scala
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mxnet.javaapi
+
+import org.apache.mxnet.GeneratorBase
+
+import scala.annotation.StaticAnnotation
+import scala.collection.mutable.ListBuffer
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox
+
+private[mxnet] class AddJNDArrayAPIs(isContrib: Boolean) extends StaticAnnotation {
+  private[mxnet] def macroTransform(annottees: Any*) = macro JavaNDArrayMacro.typeSafeAPIDefs
+}
+
+private[mxnet] object JavaNDArrayMacro extends GeneratorBase {
+
+  // scalastyle:off havetype
+  def typeSafeAPIDefs(c: blackbox.Context)(annottees: c.Expr[Any]*) = {
+    typeSafeAPIImpl(c)(annottees: _*)
+  }
+  // scalastyle:off havetype
+
+  private def typeSafeAPIImpl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
+    import c.universe._
+
+    val isContrib: Boolean = c.prefix.tree match {
+      case q"new AddJNDArrayAPIs($b)" => c.eval[Boolean](c.Expr(b))
+    }
+    // Defines Operators that should not generated
+    val notGenerated = Set("Custom")
+
+    val newNDArrayFunctions = functionsToGenerate(false, false, true)
+      .filterNot(ele => notGenerated.contains(ele.name)).groupBy(_.name.toLowerCase).map(ele => {
+      /* Pattern matching for not generating deprecated method
+       * Group all method name in lowercase
+       * Kill the capital lettered method such as Cast vs cast
+       * As it defined by default it deprecated
+       */
+      if (ele._2.length == 1) ele._2.head
+      else {
+        if (ele._2.head.name.head.isLower) ele._2.head
+        else ele._2.last
+      }
+    })
+
+    val functionDefs = ListBuffer[DefDef]()
+    val classDefs = ListBuffer[ClassDef]()
+
+    newNDArrayFunctions.foreach { ndarrayfunction =>
+
+      val useParamObject = ndarrayfunction.listOfArgs.count(arg => arg.isOptional) >= 2
+      // Construct argument field with all required args
+      var argDef = ListBuffer[String]()
+      // Construct function Implementation field (e.g norm)
+      var impl = ListBuffer[String]()
+      impl += "val map = scala.collection.mutable.Map[String, Any]()"
+      impl +=
+        "val args= scala.collection.mutable.ArrayBuffer.empty[org.apache.mxnet.NDArray]"
+      ndarrayfunction.listOfArgs.foreach({ ndarrayArg =>
+        // var is a special word used to define variable in Scala,
+        // need to changed to something else in order to make it work
+        var currArgName = ndarrayArg.safeArgName
+        if (useParamObject) currArgName = s"po.get${currArgName.capitalize}()"
+        argDef += s"$currArgName : ${ndarrayArg.argType}"
+        // NDArray arg implementation
+        val returnType = "org.apache.mxnet.javaapi.NDArray"
+        val base =
+          if (ndarrayArg.argType.equals(returnType)) {
+            s"args += $currArgName"
+          } else if (ndarrayArg.argType.equals(s"Array[$returnType]")){
+            s"$currArgName.foreach(args+=_)"
+          } else {
+            "map(\"" + ndarrayArg.argName + "\") = " + currArgName
+          }
+        impl.append(
+          if (ndarrayArg.isOptional) s"if ($currArgName != null) $base"
+          else base
+        )
+      })
+      // add default out parameter
+      argDef += s"out: org.apache.mxnet.javaapi.NDArray"
+      if (useParamObject) {
+        impl += "if (po.getOut() != null) map(\"out\") = po.getOut()"
+      } else {
+        impl += "if (out != null) map(\"out\") = out"
+      }
+      val returnType = "Array[org.apache.mxnet.javaapi.NDArray]"
+      // scalastyle:off
+      // Combine and build the function string
+      impl += "val finalArr = org.apache.mxnet.NDArray.genericNDArrayFunctionInvoke(\"" +
+        ndarrayfunction.name + "\", args.toSeq, map.toMap).arr"
+      impl += "finalArr.map(ele => new NDArray(ele))"
+      if (useParamObject) {
+        val funcDef =
+          s"""def ${ndarrayfunction.name}(po: ${ndarrayfunction.name}Param): $returnType = {
+             |  ${impl.mkString("\n")}
+             | }""".stripMargin
+        functionDefs += c.parse(funcDef).asInstanceOf[DefDef]
+      } else {
+        val funcDef =
+          s"""def ${ndarrayfunction.name}(${argDef.mkString(",")}): $returnType = {
+             |  ${impl.mkString("\n")}
+             | }""".stripMargin
+        functionDefs += c.parse(funcDef).asInstanceOf[DefDef]
+      }
+    }
+    structGeneration(c)(functionDefs.toList, annottees : _*)
+  }
+}
diff --git a/scala-package/macros/src/main/scala/org/apache/mxnet/utils/CToScalaUtils.scala b/scala-package/macros/src/main/scala/org/apache/mxnet/utils/CToScalaUtils.scala
index d0ebe5b1d2c..2fd8b2e73c7 100644
--- a/scala-package/macros/src/main/scala/org/apache/mxnet/utils/CToScalaUtils.scala
+++ b/scala-package/macros/src/main/scala/org/apache/mxnet/utils/CToScalaUtils.scala
@@ -21,19 +21,20 @@ private[mxnet] object CToScalaUtils {
 
 
   // Convert C++ Types to Scala Types
-  def typeConversion(in : String, argType : String = "",
-                     argName : String, returnType : String) : String = {
+  def typeConversion(in : String, argType : String = "", argName : String,
+                     returnType : String) : String = {
+    val header = returnType.split("\\.").dropRight(1)
     in match {
-      case "Shape(tuple)" | "ShapeorNone" => "org.apache.mxnet.Shape"
+      case "Shape(tuple)" | "ShapeorNone" => s"${header.mkString(".")}.Shape"
       case "Symbol" | "NDArray" | "NDArray-or-Symbol" => returnType
       case "Symbol[]" | "NDArray[]" | "NDArray-or-Symbol[]" | "SymbolorSymbol[]"
       => s"Array[$returnType]"
-      case "float" | "real_t" | "floatorNone" => "org.apache.mxnet.Base.MXFloat"
-      case "int" | "intorNone" | "int(non-negative)" => "Int"
-      case "long" | "long(non-negative)" => "Long"
-      case "double" | "doubleorNone" => "Double"
+      case "float" | "real_t" | "floatorNone" => "java.lang.Float"
+      case "int" | "intorNone" | "int(non-negative)" => "java.lang.Integer"
+      case "long" | "long(non-negative)" => "java.lang.Long"
+      case "double" | "doubleorNone" => "java.lang.Double"
       case "string" => "String"
-      case "boolean" | "booleanorNone" => "Boolean"
+      case "boolean" | "booleanorNone" => "java.lang.Boolean"
       case "tupleof<float>" | "tupleof<double>" | "tupleof<>" | "ptr" | "" => "Any"
       case default => throw new IllegalArgumentException(
         s"Invalid type for args: $default\nString argType: $argType\nargName: $argName")
@@ -52,8 +53,8 @@ private[mxnet] object CToScalaUtils {
     * @param argType Raw arguement Type description
     * @return (Scala_Type, isOptional)
     */
-  def argumentCleaner(argName: String,
-                      argType : String, returnType : String) : (String, Boolean) = {
+  def argumentCleaner(argName: String, argType : String,
+                      returnType : String) : (String, Boolean) = {
     val spaceRemoved = argType.replaceAll("\\s+", "")
     var commaRemoved : Array[String] = new Array[String](0)
     // Deal with the case e.g: stype : {'csr', 'default', 'row_sparse'}
diff --git a/scala-package/macros/src/test/scala/org/apache/mxnet/MacrosSuite.scala b/scala-package/macros/src/test/scala/org/apache/mxnet/MacrosSuite.scala
index c3a7c58c1af..4404b0885d5 100644
--- a/scala-package/macros/src/test/scala/org/apache/mxnet/MacrosSuite.scala
+++ b/scala-package/macros/src/test/scala/org/apache/mxnet/MacrosSuite.scala
@@ -36,7 +36,7 @@ class MacrosSuite extends FunSuite with BeforeAndAfterAll {
     )
     val output = List(
       ("org.apache.mxnet.Symbol", true),
-      ("Int", false),
+      ("java.lang.Integer", false),
       ("org.apache.mxnet.Shape", true),
       ("String", true),
       ("Any", false)
diff --git a/scala-package/mxnet-demo/java-demo/Makefile b/scala-package/mxnet-demo/java-demo/Makefile
new file mode 100644
index 00000000000..340a50f7596
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/Makefile
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+SCALA_VERSION_PROFILE := 2.11
+SCALA_VERSION := 2.11.8
+MXNET_VERSION := 1.3.1-SNAPSHOT
+
+ifeq ($(OS),Windows_NT)
+	UNAME_S := Windows
+else
+	UNAME_S := $(shell uname -s)
+endif
+
+ifeq ($(UNAME_S), Windows)
+	# TODO: currently scala package does not support windows
+	SCALA_PKG_PROFILE := windows
+else
+	ifeq ($(UNAME_S), Darwin)
+		SCALA_PKG_PROFILE := osx-x86_64-cpu
+	else
+		SCALA_PKG_PROFILE := linux-x86_64
+		ifeq ($(USE_CUDA), 1)
+        	SCALA_PKG_PROFILE := $(SCALA_PKG_PROFILE)-gpu
+        else
+        	SCALA_PKG_PROFILE := $(SCALA_PKG_PROFILE)-cpu
+        endif
+	endif
+endif
+
+javademo:
+	(mvn package -Dmxnet.profile=$(SCALA_PKG_PROFILE) \
+		-Dmxnet.scalaprofile=$(SCALA_VERSION_PROFILE) \
+		-Dmxnet.version=$(MXNET_VERSION) \
+		-Dscala.version=$(SCALA_VERSION))
+
+javaclean:
+	(mvn clean -Dmxnet.profile=$(SCALA_PKG_PROFILE) \
+		-Dmxnet.scalaprofile=$(SCALA_VERSION_PROFILE) \
+		-Dmxnet.version=$(MXNET_VERSION) \
+		-Dscala.version=$(SCALA_VERSION))
\ No newline at end of file
diff --git a/scala-package/mxnet-demo/java-demo/README.md b/scala-package/mxnet-demo/java-demo/README.md
new file mode 100644
index 00000000000..ffe614a2928
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/README.md
@@ -0,0 +1,76 @@
+# MXNet Java Sample Project
+This is an project created to use Maven-published Scala/Java package with two Java examples.
+## Setup
+Please copy the downloaded MXNet Java package jar file under the `java-demo` folder.
+
+User are required to use `mvn package` to build the package,
+ which are shown below:
+```Bash
+export SCALA_VERSION_PROFILE=2.11 SCALA_VERSION=2.11.8 MXNET_VERSION=1.3.1-SNAPSHOT
+export SCALA_PKG_PROFILE=
+mvn package -Dmxnet.profile=$(SCALA_PKG_PROFILE) \
+		-Dmxnet.scalaprofile=$(SCALA_VERSION_PROFILE) \
+		-Dmxnet.version=$(MXNET_VERSION) \
+		-Dscala.version=$(SCALA_VERSION)
+```
+These environment variable (`SCALA_PKG_PROFILE`, `SCALA_VERSION_PROFILE`, `MXNET_VERSION`, `SCALA_VERSION`)
+should be set before executing the line above.
+ 
+You can also use the `Makefile` as an alternative to do the same thing. Simply do the following:
+```Bash
+make javademo
+```
+This will load the default parameter for all the environment variable.
+ If you want to run with GPU on Linux, just simply add `USE_CUDA=1` when you run the make file
+
+## Run
+### Hello World
+The Scala file is being executed using Java. You can execute the helloWorld example as follows:
+```Bash
+java -cp $CLASSPATH sample.HelloWorld
+```
+However, you have to define the Classpath before you run the demo code. More information can be found in the `demo.sh` And you can run the bash script as follows:
+```Bash
+bash bin/java_sample.sh
+```
+It will load the library automatically and run the example
+### Object Detection using Inference API
+We also provide an example to do object detection, which downloads a ImageNet trained resnet50 model and runs inference on an image to return the classification result as
+```Bash
+Class: car
+Probabilties: 0.99847263
+Coord:312.21335, 72.02908, 456.01443, 150.66176
+Class: bicycle
+Probabilties: 0.9047381
+Coord:155.9581, 149.96365, 383.83694, 418.94516
+Class: dog
+Probabilties: 0.82268167
+Coord:83.82356, 179.14001, 206.63783, 476.78754
+```
+
+you can run using the command shown below:
+```Bash
+java -cp $CLASSPATH sample.ObjectDetection
+```
+or script as follows:
+```Bash
+bash bin/run_od.sh
+```
+
+If you want to test run on GPU, you can set a environment variable as follows:
+```Bash
+export SCALA_TEST_ON_GPU=1
+```
+## Clean up
+Clean up for Maven package is simple, you can run the pre-configed `Makefile` as:
+```Bash
+make javaclean
+```
+
+## Q & A
+If you are facing opencv issue on Ubuntu, please try as follows to install opencv 3.4 (required by 1.2.0 package and above)
+```Bash
+sudo add-apt-repository ppa:timsc/opencv-3.4
+sudo apt-get update
+sudo apt install libopencv-imgcodecs3.4
+```
\ No newline at end of file
diff --git a/scala-package/mxnet-demo/java-demo/bin/java_sample.sh b/scala-package/mxnet-demo/java-demo/bin/java_sample.sh
new file mode 100644
index 00000000000..50e7fb9eb97
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/bin/java_sample.sh
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#!/bin/bash
+CURR_DIR=$(cd $(dirname $0)/../; pwd)
+CLASSPATH=$CLASSPATH:$CURR_DIR/target/*:$CLASSPATH:$CURR_DIR/*
+java -Xmx8G  -cp $CLASSPATH sample.HelloWorld
\ No newline at end of file
diff --git a/scala-package/mxnet-demo/java-demo/bin/run_od.sh b/scala-package/mxnet-demo/java-demo/bin/run_od.sh
new file mode 100644
index 00000000000..5cbc53fbcef
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/bin/run_od.sh
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#!/bin/bash
+CURR_DIR=$(cd $(dirname $0)/../; pwd)
+
+CLASSPATH=$CLASSPATH:$CURR_DIR/target/*:$CLASSPATH:$CURR_DIR/*
+java -Xmx8G  -cp $CLASSPATH sample.ObjectDetection
\ No newline at end of file
diff --git a/scala-package/mxnet-demo/java-demo/pom.xml b/scala-package/mxnet-demo/java-demo/pom.xml
new file mode 100644
index 00000000000..5014d2e09f5
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>Demo</groupId>
+    <artifactId>mxnet-java-demo</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>MXNet Java Demo</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.mxnet</groupId>
+            <artifactId>mxnet-full_${mxnet.scalaprofile}-${mxnet.profile}</artifactId>
+            <version>${mxnet.version}</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/mxnet-full_${mxnet.scalaprofile}-${mxnet.profile}-${mxnet.version}.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/scala-package/mxnet-demo/java-demo/src/main/java/sample/HelloWorld.java b/scala-package/mxnet-demo/java-demo/src/main/java/sample/HelloWorld.java
new file mode 100644
index 00000000000..60619dc8a80
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/src/main/java/sample/HelloWorld.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample;
+
+import org.apache.mxnet.javaapi.*;
+import java.util.Arrays;
+
+public class HelloWorld {
+    public static void main(String[] args) {
+    	System.out.println("Hello World!");
+        NDArray nd = new NDArray(new float[]{2.0f, 3.0f}, new Shape(new int[]{1, 2}), Context.cpu());
+        System.out.println(nd.shape());
+    }
+}
diff --git a/scala-package/mxnet-demo/java-demo/src/main/java/sample/ObjectDetection.java b/scala-package/mxnet-demo/java-demo/src/main/java/sample/ObjectDetection.java
new file mode 100644
index 00000000000..bf9a93ae821
--- /dev/null
+++ b/scala-package/mxnet-demo/java-demo/src/main/java/sample/ObjectDetection.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample;
+import org.apache.mxnet.infer.javaapi.ObjectDetectorOutput;
+import org.apache.mxnet.javaapi.*;
+import org.apache.mxnet.infer.javaapi.ObjectDetector;
+import org.apache.commons.io.FileUtils;
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ObjectDetection {
+    private static String modelPath;
+    private static String imagePath;
+
+    private static void downloadUrl(String url, String filePath) {
+        File tmpFile = new File(filePath);
+        if (!tmpFile.exists()) {
+            try {
+                FileUtils.copyURLToFile(new URL(url), tmpFile);
+            } catch (Exception exception) {
+                System.err.println(exception);
+            }
+        }
+    }
+
+    public static void downloadModelImage() {
+        String tempDirPath = System.getProperty("java.io.tmpdir");
+        System.out.println("tempDirPath: %s".format(tempDirPath));
+        imagePath = tempDirPath + "/inputImages/resnetssd/dog-ssd.jpg";
+        String imgURL = "https://s3.amazonaws.com/model-server/inputs/dog-ssd.jpg";
+        downloadUrl(imgURL, imagePath);
+        modelPath = tempDirPath + "resnetssd/resnet50_ssd_model";
+        System.out.println("Download model files, this can take a while...");
+        String modelURL = "https://s3.amazonaws.com/model-server/models/resnet50_ssd/";
+        downloadUrl(modelURL + "resnet50_ssd_model-symbol.json",
+                tempDirPath + "/resnetssd/resnet50_ssd_model-symbol.json");
+        downloadUrl(modelURL + "resnet50_ssd_model-0000.params",
+                tempDirPath + "/resnetssd/resnet50_ssd_model-0000.params");
+        downloadUrl(modelURL + "synset.txt",
+                tempDirPath + "/resnetssd/synset.txt");
+    }
+
+    static List<List<ObjectDetectorOutput>>
+    runObjectDetectionSingle(String modelPathPrefix, String inputImagePath, List<Context> context) {
+        Shape inputShape = new Shape(new int[] {1, 3, 512, 512});
+        List<DataDesc> inputDescriptors = new ArrayList<DataDesc>();
+        inputDescriptors.add(new DataDesc("data", inputShape, DType.Float32(), "NCHW"));
+        ObjectDetector objDet = new ObjectDetector(modelPathPrefix, inputDescriptors, context, 0);
+        return objDet.imageObjectDetect(ObjectDetector.loadImageFromFile(inputImagePath), 3);
+    }
+
+    public static void main(String[] args) {
+        List<Context> context = new ArrayList<Context>();
+        if (System.getenv().containsKey("SCALA_TEST_ON_GPU") &&
+                Integer.valueOf(System.getenv("SCALA_TEST_ON_GPU")) == 1) {
+            context.add(Context.gpu());
+        } else {
+            context.add(Context.cpu());
+        }
+        downloadModelImage();
+        Shape inputShape = new Shape(new int[] {1, 3, 512, 512});
+        Shape outputShape = new Shape(new int[] {1, 6132, 6});
+        int width = inputShape.get(2);
+        int height = inputShape.get(3);
+        List<List<ObjectDetectorOutput>> output
+                = runObjectDetectionSingle(modelPath, imagePath, context);
+        String outputStr = "\n";
+        for (List<ObjectDetectorOutput> ele : output) {
+            for (ObjectDetectorOutput i : ele) {
+                outputStr += "Class: " + i.getClassName() + "\n";
+                outputStr += "Probabilties: " + i.getProbability() + "\n";
+
+                List<Float> coord = Arrays.asList(i.getXMin() * width,
+                        i.getXMax() * height, i.getYMin() * width, i.getYMax() * height);
+                StringBuilder sb = new StringBuilder();
+                for (float c: coord) {
+                    sb.append(", ").append(c);
+                }
+                outputStr += "Coord:" + sb.substring(2)+ "\n";
+            }
+        }
+        System.out.println(outputStr);
+    }
+}
\ No newline at end of file
diff --git a/scala-package/mxnet-demo/Makefile b/scala-package/mxnet-demo/scala-demo/Makefile
similarity index 96%
rename from scala-package/mxnet-demo/Makefile
rename to scala-package/mxnet-demo/scala-demo/Makefile
index 227697ba2e8..458077d1390 100644
--- a/scala-package/mxnet-demo/Makefile
+++ b/scala-package/mxnet-demo/scala-demo/Makefile
@@ -17,7 +17,7 @@
 
 SCALA_VERSION_PROFILE := 2.11
 SCALA_VERSION := 2.11.8
-MXNET_VERSION := 1.2.0
+MXNET_VERSION := 1.3.0
 
 ifeq ($(OS),Windows_NT)
 	UNAME_S := Windows
diff --git a/scala-package/mxnet-demo/README.md b/scala-package/mxnet-demo/scala-demo/README.md
similarity index 88%
rename from scala-package/mxnet-demo/README.md
rename to scala-package/mxnet-demo/scala-demo/README.md
index e30a61a2fc1..300fc7b2e10 100644
--- a/scala-package/mxnet-demo/README.md
+++ b/scala-package/mxnet-demo/scala-demo/README.md
@@ -4,7 +4,7 @@ This is an project created to use Maven-published Scala package with two Scala e
 User are required to use `mvn package` to build the package,
  which are shown below:
 ```Bash
-export SCALA_VERSION_PROFILE=2.11 SCALA_VERSION=2.11.8 MXNET_VERSION=1.2.0
+export SCALA_VERSION_PROFILE=2.11 SCALA_VERSION=2.11.8 MXNET_VERSION=1.3.0
 export SCALA_PKG_PROFILE=
 mvn package -Dmxnet.profile=$(SCALA_PKG_PROFILE) \
 		-Dmxnet.scalaprofile=$(SCALA_VERSION_PROFILE) \
@@ -12,7 +12,9 @@ mvn package -Dmxnet.profile=$(SCALA_PKG_PROFILE) \
 		-Dscala.version=$(SCALA_VERSION)
 ```
 These environment variable (`SCALA_PKG_PROFILE`, `SCALA_VERSION_PROFILE`, `MXNET_VERSION`, `SCALA_VERSION`)
-should be set before executing the line above. 
+should be set before executing the line above.
+
+To obtain the most recent MXNet version, please click [here](https://mvnrepository.com/search?q=org.apache.mxnet)
  
 You can also use the `Makefile` as an alternative to do the same thing. Simply do the following:
 ```Bash
@@ -25,7 +27,7 @@ This will load the default parameter for all the environment variable.
 ### Hello World
 The Scala file is being executed using Java. You can execute the helloWorld example as follows:
 ```Bash
-java -Xmx8G  -cp $CLASSPATH sample.HelloWorld
+java -cp $CLASSPATH sample.HelloWorld
 ```
 However, you have to define the Classpath before you run the demo code. More information can be found in the `demo.sh` And you can run the bash script as follows:
 ```Bash
@@ -41,7 +43,7 @@ You can review the complete example [here](https://github.com/apache/incubator-m
 
 you can run using the command shown below:
 ```Bash
-java -Xmx8G  -cp $CLASSPATH sample.ImageClassificationExample
+java -cp $CLASSPATH sample.ImageClassificationExample
 ```
 or script as follows:
 ```Bash
@@ -59,7 +61,7 @@ make scalaclean
 ```
 
 ## Q & A
-If you are facing opencv issue on Ubuntu, please try as follows to install opencv 3.4 (required by 1.2.0 package)
+If you are facing opencv issue on Ubuntu, please try as follows to install opencv 3.4 (required by 1.2.0 package and above)
 ```Bash
 sudo add-apt-repository ppa:timsc/opencv-3.4
 sudo apt-get update
diff --git a/scala-package/mxnet-demo/bin/demo.sh b/scala-package/mxnet-demo/scala-demo/bin/demo.sh
similarity index 100%
rename from scala-package/mxnet-demo/bin/demo.sh
rename to scala-package/mxnet-demo/scala-demo/bin/demo.sh
diff --git a/scala-package/mxnet-demo/bin/run_im.sh b/scala-package/mxnet-demo/scala-demo/bin/run_im.sh
similarity index 100%
rename from scala-package/mxnet-demo/bin/run_im.sh
rename to scala-package/mxnet-demo/scala-demo/bin/run_im.sh
diff --git a/scala-package/mxnet-demo/pom.xml b/scala-package/mxnet-demo/scala-demo/pom.xml
similarity index 100%
rename from scala-package/mxnet-demo/pom.xml
rename to scala-package/mxnet-demo/scala-demo/pom.xml
diff --git a/scala-package/mxnet-demo/src/main/scala/sample/HelloWorld.scala b/scala-package/mxnet-demo/scala-demo/src/main/scala/sample/HelloWorld.scala
similarity index 100%
rename from scala-package/mxnet-demo/src/main/scala/sample/HelloWorld.scala
rename to scala-package/mxnet-demo/scala-demo/src/main/scala/sample/HelloWorld.scala
diff --git a/scala-package/mxnet-demo/src/main/scala/sample/ImageClassificationExample.scala b/scala-package/mxnet-demo/scala-demo/src/main/scala/sample/ImageClassificationExample.scala
similarity index 100%
rename from scala-package/mxnet-demo/src/main/scala/sample/ImageClassificationExample.scala
rename to scala-package/mxnet-demo/scala-demo/src/main/scala/sample/ImageClassificationExample.scala
diff --git a/scala-package/pom.xml b/scala-package/pom.xml
index fe78a629ed2..daa2dff0bd5 100644
--- a/scala-package/pom.xml
+++ b/scala-package/pom.xml
@@ -190,8 +190,8 @@
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.3</version>
         <configuration>
-          <source>1.6</source>
-          <target>1.6</target>
+          <source>1.7</source>
+          <target>1.7</target>
           <encoding>UTF-8</encoding>
         </configuration>
       </plugin>
@@ -215,6 +215,7 @@
         <version>2.19</version>
         <configuration>
           <skipTests>true</skipTests>
+          <useSystemClassLoader>false</useSystemClassLoader>
         </configuration>
       </plugin>
       <plugin>
diff --git a/tests/tutorials/test_sanity_tutorials.py b/tests/tutorials/test_sanity_tutorials.py
index cd3f6bfcbac..446a6c27f62 100644
--- a/tests/tutorials/test_sanity_tutorials.py
+++ b/tests/tutorials/test_sanity_tutorials.py
@@ -49,12 +49,14 @@
              'scala/mnist.md',
              'scala/index.md',
              'scala/mxnet_scala_on_intellij.md',
+             'scala/mxnet_java_install_and_run_examples.md',
              'sparse/index.md',
              'speech_recognition/index.md',
              'unsupervised_learning/index.md',
              'vision/index.md',
              'tensorrt/index.md',
-             'tensorrt/inference_with_trt.md']
+             'tensorrt/inference_with_trt.md',
+             'java/mxnet_java_on_intellij.md']
 whitelist_set = set(whitelist)
 
 def test_tutorial_downloadable():


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services