Current release: 3.0.1
xsbt-web-plugin is an sbt extension for building J2EE Web applications in Scala and Java. It is best suited for projects that:
- Need to be packaged as a .war file
- Deploy to common cloud platforms (e.g. Google App Engine, Heroku, Elastic Beanstalk, Jelastic)
- Deploy to production J2EE environments (e.g. Tomcat, Jetty, GlassFish, WebSphere)
- Incorporate J2EE libraries (e.g. JSP, JSF, EJB)
- Utilize J2EE technologies (e.g.
Servlet
,Filter
, JNDI)
For previous releases, see the docs directory. Releases follow Specified Versioning guidelines.
- Scala 2.10.2+
- sbt 0.13.6+
Add xsbt-web-plugin to project/plugins.sbt:
addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "3.0.1")
Enable the Jetty plugin:
build.sbt:
enablePlugins(JettyPlugin)
From the sbt console:
- Start (or restart) the container with
jetty:start
- Stop the container with
jetty:stop
- Build a .war file with
package
To use Tomcat instead of Jetty:
- Substitute
TomcatPlugin
forJettyPlugin
- Substitute
tomcat:start
forjetty:start
- Substitute
tomcat:stop
forjetty:stop
Create a new empty project:
mkdir myproject
cd myproject
Set up the project structure:
mkdir project
mkdir -p src/main/scala
mkdir -p src/main/webapp/WEB-INF
Configure sbt:
project/build.properties:
sbt.version=0.13.8
project/build.sbt:
addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "3.0.1")
build.sbt:
scalaVersion := "2.11.6"
libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided"
enablePlugins(JettyPlugin)
Add a servlet:
src/main/scala/servlets.scala:
package servlets
import javax.servlet.http._
class MyServlet extends HttpServlet {
override def doGet(request: HttpServletRequest, response: HttpServletResponse) {
response.setContentType("text/html")
response.setCharacterEncoding("UTF-8")
response.getWriter.write("""<h1>Hello, world!</h1>""")
}
}
src/main/webapp/WEB-INF/web.xml:
<web-app>
<servlet>
<servlet-name>my servlet</servlet-name>
<servlet-class>servlets.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>my servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
xsbt-web-plugin supports sbt's triggered execution by prefixing
commands with ~
.
sbt console:
> ~jetty:start
This starts the Jetty container, then monitors the sources, resources, and webapp directories for changes, which triggers a container restart.
To pass extra arguments to the Jetty or Tomcat container, set
containerArgs
:
containerArgs := Seq("--path", "/myservice")
- For available Jetty arguments, see the Jetty Runner docs
- For available Tomcat arguments, see webapp-runner#options
To use a custom J2EE container, e.g. a main class named runner.Run
,
enable ContainerPlugin
and set containerLibs
and
containerLaunchCmd
:
enablePlugins(ContainerPlugin)
containerLibs in Container := Seq(
"org.eclipse.jetty" % "jetty-webapp" % "9.1.0.v20131115"
, "org.eclipse.jetty" % "jetty-plus" % "9.1.0.v20131115"
, "test" %% "runner" % "0.1.0-SNAPSHOT"
)
containerLaunchCmd in Container :=
{ (port, path) => Seq("runner.Run", port.toString, path) }
sbt:
> container:start
> container:stop
Example: container/custom-runner
To set system properties for the forked container JVM, set
containerForkOptions
:
containerForkOptions := new ForkOptions(runJVMOptions = Seq("-Dh2g2=42"))
Example: container/fork-options
Alternatively, set javaOptions
in the Jetty
(or Tomcat
)
configuration:
javaOptions in Jetty += "-Dh2g2=42"
Example: container/java-options
To attach a debugger, set -Xdebug
and -Xrunjdwp
:
build.sbt:
javaOptions in Jetty ++= Seq(
"-Xdebug",
"-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
)
In Eclipse:
- Create and run a new Remote Java Application launch configuration
- Set Connection Type to Scala debugger (Socket Attach)
- Configure to connect to localhost on port 8000
In IntelliJ IDEA:
- Add a Remote run configuration: Run -> Edit Configurations...
- Under Defaults select Remote and push
+
to add a new configuration - By default the configuration uses port 5005; update it to 8000 as above
- Name this configuration, and run it in debug mode
To enable debugging through JDWP, use jetty:debug
or
tomcat:debug
. Optionally set debugAddress
, which defaults to
"debug"
under Windows and "8888"
otherwise, and debugOptions
,
which defaults to:
port =>
Seq( "-Xdebug"
, Seq( "-Xrunjdwp:transport=dt_socket"
, "address=" + port
, "server=y"
, "suspend=n"
).mkString(",")
)
By default, Jetty 9.4.1 is used. To use a different version, set
containerLibs
:
containerLibs in Jetty := Seq("org.mortbay.jetty" % "jetty-runner" % "7.0.0.v20091005" intransitive())
Depending on the version, it may also be necessary to specify the name of Jetty's runner:
containerMain := "org.mortbay.jetty.runner.Runner"
Examples:
By default, the container runs on port 8080. To use a different port,
set containerPort
:
containerPort := 9090
Examples:
To use a jetty.xml configuration file, set containerConfigFile
:
containerConfigFile := Some(file("etc/jetty.xml"))
This option can be used to enable SSL and HTTPS.
Examples:
Examples:
By default, Tomcat 8.5.9.0 is used. To use a different version, set
containerLibs
:
containerLibs in Tomcat := Seq("com.github.jsimone" % "webapp-runner" % "7.0.34.1" intransitive())
Depending on the version, it may also be necessary to specify the name of Tomcat's runner:
containerMain in Tomcat := "webapp.runner.launch.Main"
This can be useful for keeping the version number out of the .war file name, using a non-conventional file name or path, adding additional information to the file name, etc.
artifactName := { (v: ScalaVersion, m: ModuleID, a: Artifact) =>
a.name + "." + a.extension
}
See "Modifying default artifacts" in the sbt documentation for additional information.
After the /target/webapp directory is prepared, it can be
modified with an arbitrary File => Unit
function by setting
webappPostProcess
.
To list the contents of the webapp directory after it is prepared:
webappPostProcess := {
webappDir: File =>
def listFiles(level: Int)(f: File): Unit = {
val indent = ((1 until level) map { _ => " " }).mkString
if (f.isDirectory) {
streams.value.log.info(indent + f.getName + "/")
f.listFiles foreach { listFiles(level + 1) }
} else streams.value.log.info(indent + f.getName)
}
listFiles(1)(webappDir)
}
To include webapp resources from multiple directories in the prepared webapp directory:
webappPostProcess := {
webappDir: File =>
val baseDir = baseDirectory.value / "src" / "main"
IO.copyDirectory(baseDir / "webapp1", webappDir)
IO.copyDirectory(baseDir / "webapp2", webappDir)
IO.copyDirectory(baseDir / "webapp3", webappDir)
}
Examples:
Files in the extra resource directory are not compiled, and are bundled directly in the project artifact .jar file.
To add a custom resources directory, set unmanagedResourceDirectories
:
unmanagedResourceDirectories in Compile <+= (sourceDirectory in Compile)(_ / "extra")
Example: webapp/unmanaged-resources
Scala files in the extra source directory are compiled, and bundled in the project artifact .jar file.
To add a custom sources directory, set unmanagedSourceDirectories
:
unmanagedSourceDirectories in Compile <+= (sourceDirectory in Compile)(_ / "extra")
Example: webapp/unmanaged-sources
By default, project classes are packaged into a .jar file, shipped in
the WEB-INF/lib directory of the .war file. To instead keep them
extracted in WEB-INF/classes, set webappWebInfClasses
:
webappWebInfClasses := true
Examples:
The Web application destination directory is where the static Web content, compiled Scala classes, library .jar files, etc. are placed. By default, they go to /target/webapp.
To specify a different directory, set target
in the webappPrepare
configuration:
target in webappPrepare := target.value / "WebContent"
Example: webapp/webapp-dest
The Web application resources directory is where static Web content (including .html, .css, and .js files, the web.xml container configuration file, etc. By default, this is kept in /src/main/webapp.
To specify a different directory, set sourceDirectory
in the
webappPrepare
configuration:
sourceDirectory in webappPrepare := (sourceDirectory in Compile).value / "WebContent"
Example: webapp/webapp-src
For situations when the prepared /target/webapp directory is needed, but the packaged .war file isn't.
sbt console:
webappPrepare
Manifest attributes of the .war file can be configured via
packageOptions in sbt.Keys.package
in build.sbt:
packageOptions in sbt.Keys.`package` +=
Package.ManifestAttributes( java.util.jar.Attributes.Name.SEALED -> "true" )
To configure the .war file to inherit the manifest attributes of the
.jar file, typically set via packageOptions in (Compile, packageBin)
, set inheritJarManifest
to true
:
inheritJarManifest := true
By default, sbt will shutdown the running container when exiting sbt.
To allow the container to continue running after sbt exits, set
containerShutdownOnExit
:
containerShutdownOnExit := false
Enable the HerokuDeploy
plugin and configure your app name:
enablePlugins(HerokuDeploy)
herokuAppName := "my-heroku-app"
Either install the Heroku Toolbelt, or
set your Heroku API key as an environment variable, launch sbt, and
deploy with herokuDeploy
:
$ HEROKU_API_KEY="xxx-xxx-xxxx" sbt
> herokuDeploy
Check out your deployed application at
https://my-heroku-app.herokuapp.com
.
Before trying to deploy anything, create an application and a Tomcat-based environment for it in Elastic Beanstalk.
Enable the ElasticBeanstalkDeployPlugin
plugin, and configure your
application's name, environment, and region:
enablePlugins(ElasticBeanstalkDeployPlugin)
elasticBeanstalkAppName := "my-elastic-beanstalk-app"
elasticBeanstalkEnvName := "production"
elasticBeanstalkRegion := "us-west-1"
Add AWS credentials to your environment, launch sbt, and deploy with
elasticBeanstalkDeploy
:
$ AWS_ACCESS_KEY="xxx" AWS_SECRET_KEY="xxx" sbt
> elasticBeanstalkDeploy
Check out your deployed application at
http://my-elastic-beanstalk-app.us-west-1.elasticbeanstalk.com
.
To start the container from the command line and block sbt from exiting
prematurely, use jetty:join
:
$ sbt jetty:start jetty:join
This is useful for running sbt in production (e.g. in a Docker container).
The development cycle can be sped up by serving static resources directly from source, and avoiding packaging of compiled artifacts.
Use <container>:quickstart
in place of <container>:start
to run the
container in quickstart mode:
> jetty:quickstart
Note that this necessarily circumvents any behavior set in
webappPostProcess
.
To launch using more than a single container, set containerScale
:
containerScale := 5
This will configure the container to launch in five forked JVMs, using
five sequential ports starting from containerPort
.
In debug mode, five additional sequential debug ports starting from
debugPort
will be opened.
The development cycle can be further sped up by skipping server restarts between code recompilation.
Add -agentpath
to the container's JVM options:
javaOptions in Jetty += "-agentpath:/path/to/jrebel/lib/libjrebel64.so"
Launch the container with quickstart
, and run triggered compilation:
> jetty:quickstart
> ~compile