Here’s a github repo which will track this project

For our next move in the scalatra+docker project, let’s take the steps to go from an API we can run on localhost to a configuration that we can deploy.

Since Scala complies to Java bytecode and runs on the JVM, we need to create a “fat JAR” out of our application code. A JAR is a “Java Archive” file, and a fat JAR is basically a bundle of all the code we need to run our application, including all the dependencies, bundled into a single file.

I will admit that I am not an expert in Java build systems so I am going to hack my way thru this =)

The package we will use to assemble our fat JAR is appropriately called sbt-assembly. Let’s add this dependency to our project.

touch project/assembly.sbt

In that file, enter the following line:

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.4")

Now refresh the project using IntelliJ.

So if you’ve ever done any Java programming you are familiar with the PSVM or public static void main method. This main method is the entry point to an application. And you may have also noticed that our project is missing a main method, something required in order to create a fat JAR. So lets add the Scalatra version of a main method.

First, in the src/main/scala/com/lombardo/app/ path, let’s add a file called JettyLauncher.scala. (try doing this with IntelliJ)

Now for the code. It lives here in a Scalatra tutorial.

Here’s what my file looks like:

package com.lombardo.app
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.{DefaultServlet, ServletContextHandler}
import org.eclipse.jetty.webapp.WebAppContext
import org.scalatra.servlet.ScalatraListener

object JettyLauncher {
  def main(args: Array[String]) {
    val port = if(System.getenv("PORT") != null) System.getenv("PORT").toInt else 8080

    val server = new Server(port)
    val context = new WebAppContext()
    context setContextPath "/"
    context.setResourceBase("src/main/webapp")
    context.addEventListener(new ScalatraListener)
    context.addServlet(classOf[DefaultServlet], "/")

    server.setHandler(context)

    server.start
    server.join
  }
}

Now I’m sure your IntelliJ file is as red as a blood bank since we just brought in a bunch of dependencies that are not in our project yet. Let’s fix that. As the tutorial says, in build.sbt we just need to give our jetty-webapp dependency the compile directive.

// from "org.eclipse.jetty" % "jetty-webapp" % "9.2.15.v20160210" % "container",
// to "org.eclipse.jetty" % "jetty-webapp" % "9.2.15.v20160210" % "container;compile",

Once we refresh our dependencies all the red in JettyLauncher object will be gone.

What we have done here is created a Jetty server that can launch our application independently. The final step is to let our build system sbt know where this main method lies. In build.sbt just add this line:

mainClass in assembly := Some("com.lombardo.app.JettyLauncher")

Now let’s build that fat JAR! From the terminal enter:

sbt assembly

At the end of this process we see some output like:

[info] Packaging demo-api/target/scala-2.12/demo-api-assembly-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.

Sweet. Our fat JAR is ready to roll. Now let’s issue the command to start the application using the Java Runtime Environment (JRE).

note: its important to grab the entire file path to your JAR file. Your path may be different than mine.

java -jar demo-api/target/scala-2.12/demo-api-assembly-0.1.0-SNAPSHOT.jar

You should see some familiar output in the terminal! That’s our app starting from the JVM. We are so close to Dockerizing this sucker. But one thing at a time. Let’s verify its behavior first.

Hit http://localhost:8080/greetings with curl or postman and BOOM there’s our RESTful payload, generated by our packaged Scalatra app.

check out this commit to see the code added during this post