I implemented a Scalatra servlet and now want to create an executable jar, just like described in this tutorial: http://www.scalatra.org/2.2/guides/deployment/standalone.htm
There are two options of standalone deployment currently:
scalatra-sbt plugin, contains a start shell script, the runtime resources and the webapp resources in folders.For a standalone .jar file using sbt-assembly you need to add the plugin first to project/build.sbt:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
Then you need to modify the project build, e.g. project/build.scala. Import the plugin's settings and keys:
import sbtassembly.Plugin._
import sbtassembly.Plugin.AssemblyKeys._
With that you can create settings for the sbt-assembly plugin:
// settings for sbt-assembly plugin
val myAssemblySettings = assemblySettings ++ Seq(
// handle conflicts during assembly task
mergeStrategy in assembly <<= (mergeStrategy in assembly) {
(old) => {
case "about.html" => MergeStrategy.first
case x => old(x)
}
},
// copy web resources to /webapp folder
resourceGenerators in Compile <+= (resourceManaged, baseDirectory) map {
(managedBase, base) =>
val webappBase = base / "src" / "main" / "webapp"
for {
(from, to) <- webappBase ** "*" x rebase(webappBase, managedBase / "main" / "webapp")
} yield {
Sync.copy(from, to)
to
}
}
)
The first defines a merge strategy, the last one copies the static web resources from src/main/webapp to <resourceManaged>/main/webapp. They will be included in the final .jar in a sub-folder /webapp.
Include the settings in your project:
lazy val project = Project("myProj", file(".")).settings(mySettings: _*).settings(myAssemblySettings:_*)
Now the launcher needs to be created. Note how the resource base is set:
import org.eclipse.jetty.server.nio.SelectChannelConnector
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.webapp.WebAppContext
import org.scalatra.servlet.ScalatraListener
object JettyMain {
def run = {
val server = new Server
val connector = new SelectChannelConnector
connector.setPort(8080)
server.addConnector(connector)
val context = new WebAppContext
context.setContextPath("/")
val resourceBase = getClass.getClassLoader.getResource("webapp").toExternalForm
context.setResourceBase(resourceBase)
context.setEventListeners(Array(new ScalatraListener))
server.setHandler(context)
server.start
server.join
}
}
scalatra-sbt PluginYou need to add those imports to your SBT build.scala:
import org.scalatra.sbt.DistPlugin._
import org.scalatra.sbt.DistPlugin.DistKeys._
Then you need to add the plugin's settings to your project. The settings are in DistPlugin.distSettings.
You can also customize your distribution and add custom memory settings, exports and command line options. Note that those are all optional:
val myDistSettings = DistPlugin.distSettings ++ Seq(
mainClass in Dist := Some("ScalatraLauncher"),
memSetting in Dist := "2g",
permGenSetting in Dist := "256m",
envExports in Dist := Seq("LC_CTYPE=en_US.UTF-8", "LC_ALL=en_US.utf-8"),
javaOptions in Dist ++= Seq("-Xss4m", "-Dfile.encoding=UTF-8")
)
On the SBT prompt you can then type dist. The .zip file will be in the target folder.
I recently ran into trouble doing this.
First, you need to make sure that jetty is available at compile time. These two lines:
"org.eclipse.jetty" % "jetty-webapp" % "8.1.8.v20121106" % "container",
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" artifacts (Artifact("javax.servlet", "jar", "jar")),
Need to have compile in them:
"org.eclipse.jetty" % "jetty-webapp" % "8.1.8.v20121106" % "compile;container",
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "compile;container;provided;test" artifacts (Artifact("javax.servlet", "jar", "jar"))
Second, from your description it sounds like sbt-assembly is not configured correctly. You need to remove (comment out) these lines:
lazy val buildSettings = Defaults.defaultSettings ++ Seq(
version := "0.1",
organization := "de.foobar",
scalaVersion := "2.10.1"
)
lazy val app = Project("app", file("app"),
settings = buildSettings ++ assemblySettings) settings(
// your settings here
)
You will need to add ++ assemblySettings to your foobar project immediately after scalateSettings. Your plugins.sbt file also needs to contain the following line in it:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
For reference, I recommend against using sbt-assembly because you will most likely run into dependency conflicts that will need to be resolved with a merge strategy. Instead I suggest you use a task that collects your dependencies into a directory (examples here and here). And then add them to the java classpath using java -cp /lib/* ....
Third, be wary of the Jetty project in Scalatra's GitHub. I used:
import java.net.InetSocketAddress
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.DefaultServlet
import org.scalatra.servlet.ScalatraListener
import org.eclipse.jetty.webapp.WebAppContext
object Jetty {
def main(args: Array[String]) = {
val socketAddress = new InetSocketAddress(8080)
val server = new Server(socketAddress)
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()
}
}
Finally, it might be worth double checking your ScalatraBootstrap is in the usual place.
Hope that helps. If not I can post my entire build.scala for you.
For an up to date answer, please refer to these two files (Credits go to Scalatra in Action book):
https://github.com/scalatra/scalatra-in-action/blob/master/chapter09-standalone/src/main/scala/ScalatraLauncher.scala
and
https://github.com/scalatra/scalatra-in-action/blob/master/chapter09-standalone/project/Build.scala