sbt-war is an sbt plugin for packaging and running .war files.
sbt-war is formerly known as xsbt-web-plugin. For documentation and source code of prior versions, browse this repository from the desired git tag. The most recent prior version is 4.2.5.
- sbt 1.x and up
- Scala 2.12.x and up
- Submit a question, bug report, or feature request as a new GitHub issue
- Look for earldouglas in the
#sbt
channel on the Scala Discord server
A project that uses sbt-war looks something like this:
$ tree my-web-project/
my-web-project/
├── build.sbt
├── project
│ ├── build.properties
│ └── plugins.sbt
└── src
├── main
│ ├── scala
│ │ └── mypackage
│ │ └── MyServlet.scala
│ └── webapp
│ ├── images
│ │ └── logo.png
│ ├── index.html
│ └── styles
│ └── style.css
└── test
└── scala
├── http.scala
└── mypackage
└── MyServletSuite.scala
A packaged .war file looks something like this:
$ sbt "show package"
[info] /path/to/my-web-project/target/scala-3.1.0/my-web-project_3-0.1.0-SNAPSHOT.war
$ jar -tf target/scala-3.1.0/my-web-project_3-0.1.0-SNAPSHOT.war | LC_COLLATE=C sort
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/classes/
WEB-INF/classes/mypackage/
WEB-INF/classes/mypackage/Servlet.class
WEB-INF/classes/mypackage/Servlet.tasty
WEB-INF/lib/
WEB-INF/lib/scala-library-2.13.6.jar
WEB-INF/lib/scala3-library_3-3.1.0.jar
images/
images/logo.png
index.html
styles/
styles/style.css
Classes under src/main/scala are compiled and included in the .war file under WEB-INF/classes.
Dependencies are copied as .jar files into the WEB-INF/lib/ directory.
Static assets (such as .html files, stylesheets, images, etc.) are copied from src/main/webapp to to the root of the .war file.
$ sbt new earldouglas/sbt-war.g8
name [My Web Project]: hello sbt-war
Template applied in ./hello-sbt-war
$ cd hello-sbt-war/
$ sbt
> warStart
$ curl localhost:8080/hello
<h1>Hello, world!</h1>
> warStop
Create a new empty project:
$ mkdir myproject
$ cd myproject
Set up the project structure:
$ mkdir project
$ mkdir -p src/main/scala/mypackage
Configure sbt:
project/build.properties:
sbt.version=1.10.2
project/plugins.sbt:
addSbtPlugin("com.earldouglas" % "sbt-war" % "5.0.0-M6")
build.sbt:
scalaVersion := "3.5.1"
enablePlugins(SbtWar)
Add a servlet:
src/main/scala/mypackage/MyServlet.scala:
package mypackage
import jakarta.servlet.annotation.WebServlet
import jakarta.servlet.http.HttpServlet
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
@WebServlet(urlPatterns = Array("/hello"))
class MyServlet extends HttpServlet:
override def doGet(
req: HttpServletRequest,
res: HttpServletResponse
): Unit =
res.setContentType("text/html")
res.setCharacterEncoding("UTF-8")
res.getWriter.write("""<h1>Hello, world!</h1>""")
Run it from sbt with warStart
:
$ sbt
> warStart
$ curl localhost:8080/hello
<h1>Hello, world!</h1>
Stop it with warStop
:
> warStop
Create a .war file with package
:
> package
Key | Type | Default | Notes |
---|---|---|---|
warResources |
Map[String,File] |
src/main/webapp | Static files (HTML, CSS, JS, images, etc.) to serve directly |
warClasses |
Map[String,File] |
project classes | .class files to copy into the WEB-INF/classes directory |
warLib |
Map[String,File] |
project libs | .jar files to copy into the WEB-INF/lib directory |
warPort |
Int |
8080 |
The local container port to use when running with warStart |
warForkOptions |
ForkOptions |
BufferedOutput |
Options for the forked JVM used when running with warStart |
Key | Notes |
---|---|
warStart |
Starts a local container, serving content directly from project sources |
warStartPackage |
Starts a local container, serving content from the packaged .war file |
warJoin |
Blocks until the container shuts down |
warStop |
Shuts down the container |
Resources are the various static files, deployment descriptors, etc. that go into a .war file.
The warResources
setting is a mapping from destination to source of
these files. The destination is a path relative to the contents of the
.war file. The source is a path on the local filesystem.
By default, everything in src/main/webapp is included.
For example, given the following .war file:
myproject.war
├── index.html
├── styles/
│ └── theme.css
├── WEB-INF/
│ └── web.xml
└── META-INF/
└── MANIFEST.MF
The warResources
mapping would look like this:
"index.html" -> File(".../src/main/webapp/index.html")
"styles/theme.css" -> File(".../src/main/webapp/styles/theme.css")
"WEB-INF/web.xml" -> File(".../src/main/webapp/WEB-INF/web.xml")
To use a different directory, e.g. src/main/WebContent:
warResources :=
(Compile / sourceDirectory)
.map(_ / "WebContent")
.map(WebappComponents.getResources)
Manifest attributes of the .war file can be configured via
packageOptions
:
sbt.Keys.`package` / packageOptions +=
Package.ManifestAttributes(
java.util.jar.Attributes.Name.SEALED -> "true"
)
By default, project classes are copied into the WEB-INF/classes
directory of the .war file. To package them in a .jar file in the
WEB-INF/lib directory instead, set exportJars
:
exportJars := true
See "Configure packaging" in the sbt documentation for additional information.
By default, all runtime dependencies are copied into the WEB-INF/lib directory.
To use a dependency at compile time but exclude it from the .war file,
set its scope to Provided
:
libraryDependencies += "foo" % "bar" % "1.0.0" % Provided
By default, the container runs on port 8080. To use a different port,
set warPort
:
warPort := 9090
To set environment variables, system properties, and more for the
forked container JVM, set a
ForkOptions
instance via warForkOptions
.
For example: to be able to attach a debugger, set -Xdebug
and
-Xrunjdwp
:
build.sbt:
warForkOptions :=
ForkOptions()
.withRunJVMOptions(
Seq(
"-Xdebug",
"-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
)
)
To run the webapp directly from project sources, use warStart
:
> warStart
To run the webapp from the packaged .war file, use warStartPackage
:
> warStartPackage
To block sbt while the container is running, use warJoin
:
$ sbt warStart warJoin
This is useful for running sbt in production (e.g. in a Docker container), if you're into that kind of thing.
To stop the running container, use warStop
:
> warStop