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