diff --git a/README.md b/README.md
index 6be08cf6..785e0801 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ Template applied in ./hello-sbt-war
$ cd hello-sbt-war/
$ sbt
-> webappStart
+> warStart
```
```
@@ -45,7 +45,7 @@ $ curl localhost:8080/hello
```
```
-> webappStop
+> warStop
```
## Getting started from scratch
@@ -109,11 +109,11 @@ class MyServlet extends HttpServlet:
res.getWriter.write("""
Hello, world!
""")
```
-Run it from sbt with `webappStart`:
+Run it from sbt with `warStart`:
```
$ sbt
-> webappStart
+> warStart
```
```
@@ -121,10 +121,10 @@ $ curl localhost:8080/hello
Hello, world!
```
-Stop it with `webappStop`:
+Stop it with `warStop`:
```
-> webappStop
+> warStop
```
Create a .war file with `package`:
@@ -135,50 +135,30 @@ Create a .war file with `package`:
## Settings
-| Key | Type | Default | Notes |
-| ------------------------------- | ------------------ | ----------------- | --------------------------------------------------------------------------------------- |
-| `webappResources` | `Map[String,File]` | *src/main/webapp* | Static files (HTML, CSS, JS, images, etc.) to serve directly |
-| `webappClasses` | `Map[String,File]` | project classes | .class files to copy into the *WEB-INF/classes* directory |
-| `webappLib` | `Map[String,File]` | project libs | .jar files to copy into the *WEB-INF/lib* directory |
-| `webappRunnerVersion` | `String` | `"10.1.28.0"` | The version of `com.heroku:webapp-runner` to use for running the webapp |
-| `webappComponentsRunnerVersion` | `String` | `"10.1.28.0-M1"` | The version of `com.earldouglas:webapp-components-runner` to use for running the webapp |
-| `webappPort` | `Int` | `8080` | The local container port to use when running with `webappStart` |
-| `warPort` | `Int` | `8080` | The local container port to use when running with `warStart` |
-| `webappForkOptions` | `ForkOptions` | Buffered output | Options for the forked JVM used when running with `webappStart` |
-| `warForkOptions` | `ForkOptions` | Buffered output | Options for the forked JVM used when running with `warStart` |
+| 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 |
+| `warRunnerVersion` | `String` | `"10.1.28.0-M1"` | The version of `com.earldouglas:webapp-components-runner` to run the webapp |
+| `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` |
## Commands
-| Key | Notes |
-| ------------- | ----------------------------------------------------------------------- |
-| `webappStart` | Starts a local container, serving content directly from project sources |
-| `webappJoin` | Blocks until the container shuts down |
-| `webappStop` | Shuts down the container |
-| `warStart` | Starts a local container, serving content from the packaged .war file |
-| `warJoin` | Blocks until the container shuts down |
-| `warStop` | Shuts down the container |
-
-### `war` vs. `webapp`
-
-Settings and commands that begin with `war` apply to the packaged .war
-file, which includes resources, classes, and libraries. The development
-cycle can be sped up by serving resources, classes, and libraries
-directly from source, avoiding the overhead of packaging a
-*.war* file.
+| Key | Notes |
+| --------------- | ----------------------------------------------------------------------- |
+| `warStart` | Starts a local container, serving content from the packaged .war file |
+| `warQuickstart` | Starts a local container, serving content directly from project sources |
+| `warJoin` | Blocks until the container shuts down |
+| `warStop` | Shuts down the container |
-Use the `webapp` prefix in place of `war` to skip packaging, and run the
-container directly from source:
+### `warResources`
-```
-> webappStart
-```
-
-### `webappResources`
+Resources are the various static files, deployment descriptors, etc.
+that go into a .war file.
-Webapp resources are the various static files, deployment descriptors,
-etc. that go into a .war file.
-
-The `webappResources` setting is a mapping from destination to source of
+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.
@@ -197,7 +177,7 @@ myproject.war
└── MANIFEST.MF
```
-The `webappResources` mapping would look like this:
+The `warResources` mapping would look like this:
```
"index.html" -> File(".../src/main/webapp/index.html")
@@ -208,7 +188,7 @@ The `webappResources` mapping would look like this:
To use a different directory, e.g. *src/main/WebContent*:
```scala
-webappResources :=
+warResources :=
(Compile / sourceDirectory)
.map(_ / "WebContent")
.map(WebappComponents.getResources)
@@ -224,7 +204,7 @@ sbt.Keys.`package` / packageOptions +=
)
```
-### `webappClasses`
+### `warClasses`
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
@@ -238,7 +218,7 @@ See ["Configure
packaging"](https://www.scala-sbt.org/1.x/docs/Howto-Package.html) in
the sbt documentation for additional information.
-### `webappLib`
+### `warLib`
By default, all runtime dependencies are copied into the *WEB-INF/lib*
directory.
@@ -250,40 +230,26 @@ set its scope to `Provided`:
libraryDependencies += "foo" % "bar" % "1.0.0" % Provided
```
-### `webappRunnerVersion`
-
-By default, [Webapp Runner](https://github.com/heroku/webapp-runner)
-10.1.x is used to run the .war file in a forked JVM. To use a different
-version, set `webappRunnerVersion`:
-
-```scala
-webappRunnerVersion := "9.0.93.0"
-```
-
-### `webappComponentsRunnerVersion`
+### `warRunnerVersion`
By default, [Webapp Components
Runner](https://github.com/earldouglas/webapp-components-runner) 10.1.x
is used to run the webapp in a forked JVM. To use a different version,
-set `webappComponentsRunnerVersion`:
+set `warRunnerVersion`:
```scala
-webappComponentsRunnerVersion := "9.0.93.0.0"
+warRunnerVersion := "9.0.93.0.0"
```
-### `warPort` and `webappPort`
+### `warPort`
By default, the container runs on port *8080*. To use a different port,
-set `warPort`/`webappPort`:
+set `warPort`:
```scala
warPort := 9090
```
-```scala
-webappPort := 9090
-```
-
### `warForkOptions`
To set environment variables, system properties, and more for the
@@ -291,7 +257,8 @@ forked container JVM, set a
[ForkOptions](https://www.scala-sbt.org/1.x/api/sbt/ForkOptions.html)
instance via `warForkOptions`.
-For example: to attach a debugger, set `-Xdebug` and `-Xrunjdwp`:
+For example: to be able to attach a debugger, set `-Xdebug` and
+`-Xrunjdwp`:
*build.sbt:*
@@ -306,46 +273,25 @@ warForkOptions :=
)
```
-### `webappForkOptions`
+### `warStart` and `warQuickstart`
-To set environment variables, system properties, and more for the
-forked container JVM, set a
-[ForkOptions](https://www.scala-sbt.org/1.x/api/sbt/ForkOptions.html)
-instance via `webappForkOptions`.
-
-For example: to attach a debugger, set `-Xdebug` and `-Xrunjdwp`:
-
-*build.sbt:*
-
-```scala
-webappForkOptions :=
- ForkOptions()
- .withRunJVMOptions(
- Seq(
- "-Xdebug",
- "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
- )
- )
-```
-
-### `webappStart` and `warStart`
-
-```
-> webappStart
-```
+To run the webapp, use `warStart`:
```
> warStart
```
-### `webappJoin` and `warJoin`
-
-These can be used to block sbt while the container is running:
+To skip packaging the .war file before launching the container, use
+`warQuickstart`:
```
-$ sbt webappStart webappJoin
+> warQuickstart
```
+### `warJoin`
+
+To block sbt while the container is running, use `warJoin`:
+
```
$ sbt warStart warJoin
```
@@ -353,14 +299,13 @@ $ 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.
-### `webappStop` and `warStop`
+### `warStop`
-These can be used to stop the running container:
-
-```
-> webappStop
-```
+To stop the running container, use `warStop`:
```
> warStop
```
+
+[`ForkOptions`]: https://www.scala-sbt.org/1.x/api/sbt/ForkOptions.html
+[`BufferedOutput`]: https://www.scala-sbt.org/1.x/api/sbt/OutputStrategy$$BufferedOutput.html
diff --git a/build.sbt b/build.sbt
index cb42974c..3d3a57d8 100644
--- a/build.sbt
+++ b/build.sbt
@@ -20,24 +20,15 @@ semanticdbVersion := scalafixSemanticdb.revision
scalacOptions += "-Ywarn-unused-import"
scalacOptions += s"-P:semanticdb:sourceroot:${baseDirectory.value}"
-// webapp-runner
-lazy val webappRunnerVersion =
- settingKey[String]("webapp-runner version")
-webappRunnerVersion := "10.1.28.0"
-libraryDependencies += "com.heroku" % "webapp-runner" % webappRunnerVersion.value % Provided
-
// webapp-components-runner
-lazy val webappComponentsRunnerVersion =
+lazy val warRunnerVersion =
settingKey[String]("webapp-components-runner version")
-webappComponentsRunnerVersion := "10.1.28.0.0-M1"
-libraryDependencies += "com.earldouglas" % "webapp-components-runner" % webappComponentsRunnerVersion.value % Provided
+warRunnerVersion := "10.1.28.0.0-M1"
+libraryDependencies += "com.earldouglas" % "webapp-components-runner" % warRunnerVersion.value % Provided
// sbt-buildinfo
enablePlugins(BuildInfoPlugin)
-buildInfoKeys := Seq[BuildInfoKey](
- webappRunnerVersion,
- webappComponentsRunnerVersion
-)
+buildInfoKeys := Seq[BuildInfoKey](warRunnerVersion)
buildInfoPackage := "com.earldouglas.sbt.war"
// Testing
diff --git a/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala b/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala
index 8bd73c39..400082ec 100644
--- a/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala
+++ b/src/main/scala/com/earldouglas/sbt/war/SbtWar.scala
@@ -1,7 +1,17 @@
package com.earldouglas.sbt.war
import sbt.AutoPlugin
+import sbt.Def.Initialize
+import sbt.Def.settingKey
+import sbt.Keys._
+import sbt.Keys.{`package` => pkg}
import sbt.Plugins
+import sbt._
+
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.util.concurrent.atomic.AtomicReference
+import scala.sys.process.{Process => ScalaProcess}
/** The top-level plugin to be used by default. From the required
* plugins, this brings in all of the webapp components mappings, .war
@@ -10,6 +20,149 @@ import sbt.Plugins
*/
object SbtWar extends AutoPlugin {
+ object autoImport {
+ lazy val War = config("war").hide
+ lazy val warPort = settingKey[Int]("container port")
+ lazy val warStart = taskKey[Unit]("start container")
+ lazy val warJoin = taskKey[Unit]("join container")
+ lazy val warStop = taskKey[Unit]("stop container")
+ lazy val warQuickstart = taskKey[Unit]("quickstart container")
+ lazy val warForkOptions =
+ taskKey[ForkOptions]("container fork options")
+ }
+
+ import autoImport._
+
override val requires: Plugins =
- WarPackageRunnerPlugin && WebappComponentsRunnerPlugin
+ WarPackagePlugin
+
+ override val projectConfigurations: Seq[Configuration] =
+ Seq(War)
+
+ private lazy val containerInstance =
+ new AtomicReference[Option[ScalaProcess]](None)
+
+ override val projectSettings: Seq[Setting[_]] = {
+
+ def stopContainerInstance(log: String => Unit): Unit =
+ containerInstance
+ .getAndSet(None)
+ .foreach { p =>
+ log("[sbt-war] Starting server")
+ p.destroy()
+ }
+
+ val runnerJars: Initialize[Task[Seq[File]]] =
+ Def.task {
+ Classpaths
+ .managedJars(War, classpathTypes.value, update.value)
+ .map(_.data)
+ .toList
+ }
+
+ val startWar: Initialize[Task[Unit]] =
+ Def.task {
+ stopContainerInstance(streams.value.log.info(_))
+
+ streams.value.log.info("[sbt-war] Starting server")
+ val process: ScalaProcess =
+ Fork.java.fork(
+ warForkOptions.value,
+ Seq(
+ "-cp",
+ Path.makeString(runnerJars.value),
+ "webapp.runner.launch.Main",
+ "--port",
+ warPort.value.toString(),
+ pkg.value.getPath()
+ )
+ )
+ containerInstance.set(Some(process))
+ }
+
+ val joinWar: Initialize[Task[Unit]] =
+ Def.task(containerInstance.get.map(_.exitValue()))
+
+ val stopWar: Initialize[Task[Unit]] =
+ Def.task(stopContainerInstance(streams.value.log.info(_)))
+
+ val onLoadSetting: Initialize[State => State] =
+ Def.setting {
+ (Global / onLoad).value
+ .compose { state: State =>
+ state.addExitHook(stopContainerInstance(println(_)))
+ }
+ }
+
+ val forkOptions: Initialize[Task[ForkOptions]] =
+ Def.task {
+ ForkOptions()
+ .withOutputStrategy(Some(BufferedOutput(streams.value.log)))
+ }
+
+ val runnerLibraries: Initialize[Seq[ModuleID]] =
+ Def.setting {
+ Seq(
+ "com.earldouglas" % "webapp-components-runner" % BuildInfo.warRunnerVersion % War
+ )
+ }
+
+ val quickstartWar: Initialize[Task[Unit]] =
+ Def.task {
+
+ val runnerConfigFile: File = {
+
+ val emptyDir: File = (Compile / target).value / "empty"
+
+ val resourceMapString =
+ WebappComponentsPlugin.warContents.value
+ .map { case (k, v) =>
+ s"${k}->${v}"
+ }
+ .mkString(",")
+
+ val configurationFile: File =
+ (Compile / target).value / "webapp-components.properties"
+
+ Files
+ .writeString(
+ Paths.get(configurationFile.getPath()),
+ s"""|hostname=localhost
+ |port=${warPort.value}
+ |contextPath=
+ |emptyWebappDir=${emptyDir}
+ |emptyClassesDir=${emptyDir}
+ |resourceMap=${resourceMapString}
+ |""".stripMargin
+ )
+ .toFile()
+ }
+
+ stopContainerInstance(streams.value.log.info(_))
+
+ streams.value.log.info("[sbt-war] Quickstarting server")
+ val process: ScalaProcess =
+ Fork.java.fork(
+ warForkOptions.value,
+ Seq(
+ "-cp",
+ Path.makeString(runnerJars.value),
+ "com.earldouglas.WebappComponentsRunner",
+ runnerConfigFile.getPath()
+ )
+ )
+ containerInstance.set(Some(process))
+ }
+
+ Seq(
+ Global / onLoad := onLoadSetting.value,
+ libraryDependencies ++= runnerLibraries.value,
+ warForkOptions := forkOptions.value,
+ warJoin := joinWar.value,
+ warPort := 8080,
+ warQuickstart := quickstartWar.value,
+ warStart := startWar.value,
+ warStop := stopWar.value
+ )
+ }
}
diff --git a/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala
index b8d0e4f9..a2a493bc 100644
--- a/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala
+++ b/src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala
@@ -20,9 +20,9 @@ object WarPackagePlugin extends AutoPlugin {
override lazy val projectSettings: Seq[Setting[_]] = {
- // Flip webappContents around from (dst -> src) to (src -> dst)
+ // Flip warContents around from (dst -> src) to (src -> dst)
val packageContents: Initialize[Task[Seq[(java.io.File, String)]]] =
- WebappComponentsPlugin.webappContents
+ WebappComponentsPlugin.warContents
.map(_.map(_.swap).toSeq)
val packageTaskSettings: Seq[Setting[_]] =
diff --git a/src/main/scala/com/earldouglas/sbt/war/WarPackageRunnerPlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WarPackageRunnerPlugin.scala
deleted file mode 100644
index 47460841..00000000
--- a/src/main/scala/com/earldouglas/sbt/war/WarPackageRunnerPlugin.scala
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.earldouglas.sbt.war
-
-import sbt.Def.Initialize
-import sbt.Def.settingKey
-import sbt.Keys._
-import sbt.Keys.{`package` => pkg}
-import sbt._
-
-import java.util.concurrent.atomic.AtomicReference
-import scala.sys.process.{Process => ScalaProcess}
-
-/** Launches the .war file managed by WarPackagePlugin. Uses a forked
- * JVM to run Tomcat via com.heroku:webapp-runner.
- */
-object WarPackageRunnerPlugin extends AutoPlugin {
-
- object autoImport {
- lazy val War = config("war").hide
- lazy val warPort = settingKey[Int]("war container port")
- lazy val warStart = taskKey[Unit]("start war container")
- lazy val warJoin = taskKey[Unit]("join war container")
- lazy val warStop = taskKey[Unit]("stop war container")
- lazy val warForkOptions =
- taskKey[ForkOptions]("war container fork options")
- lazy val warMainClass =
- settingKey[String]("war container main class")
- }
-
- import autoImport._
- import WebappRunnerPlugin.autoImport._
-
- override val requires: Plugins =
- WarPackagePlugin && WebappRunnerPlugin
-
- override val projectConfigurations: Seq[Configuration] =
- Seq(War)
-
- private lazy val containerInstance =
- new AtomicReference[Option[ScalaProcess]](None)
-
- override val projectSettings: Seq[Setting[_]] = {
-
- def stopContainerInstance(): Unit = {
- val oldProcess = containerInstance.getAndSet(None)
- oldProcess.foreach(_.destroy())
- }
-
- val startWar: Initialize[Task[Unit]] =
- Def.task {
- stopContainerInstance()
-
- val runnerJars: Seq[File] =
- Classpaths
- .managedJars(War, classpathTypes.value, update.value)
- .map(_.data)
- .toList
-
- streams.value.log.info("[sbt-war] Starting server")
- val process: ScalaProcess =
- Fork.java.fork(
- warForkOptions.value,
- Seq(
- "-cp",
- Path.makeString(runnerJars),
- warMainClass.value,
- "--port",
- warPort.value.toString(),
- pkg.value.getPath()
- )
- )
- containerInstance.set(Some(process))
- }
-
- val joinWar: Initialize[Task[Unit]] =
- Def.task(containerInstance.get.map(_.exitValue()))
-
- val stopWar: Initialize[Task[Unit]] =
- Def.task(stopContainerInstance())
-
- val onLoadSetting: Initialize[State => State] =
- Def.setting {
- (Global / onLoad).value
- .compose { state: State =>
- state.addExitHook(stopContainerInstance())
- }
- }
-
- val forkOptions: Initialize[Task[ForkOptions]] =
- Def.task {
- ForkOptions()
- .withOutputStrategy(Some(BufferedOutput(streams.value.log)))
- }
-
- val runnerLibrary: Initialize[ModuleID] =
- Def.setting {
- ("com.heroku" % "webapp-runner" % webappRunnerVersion.value intransitive ()) % War
- }
-
- Seq(
- warPort := 8080,
- warStart := startWar.value,
- warJoin := joinWar.value,
- warStop := stopWar.value,
- warForkOptions := forkOptions.value,
- Global / onLoad := onLoadSetting.value,
- libraryDependencies += runnerLibrary.value,
- warMainClass := "webapp.runner.launch.Main"
- )
- }
-}
diff --git a/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala
index 17e07119..fd8bd327 100644
--- a/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala
+++ b/src/main/scala/com/earldouglas/sbt/war/WebappComponentsPlugin.scala
@@ -11,13 +11,13 @@ import sbt._
*
* Webapp components are managed as three sets of mappings:
*
- * - webappResources: All the static HTML, CSS, JS, images, etc.
- * files to be served by the application. Also, optionally, the
+ * - warResources: All the static HTML, CSS, JS, images, etc. files
+ * to be served by the application. Also, optionally, the
* WEB-INF/web.xml deployment descriptor.
- * - webappClasses: All of the classes, etc. on the classpath to be
+ * - warClasses: All of the classes, etc. on the classpath to be
* copied into the WEB-INF/classes directory.
- * - webappLib: All of the .jar files to be copied into the
- * WEB-INF/lib directory.
+ * - warLib: All of the .jar files to be copied into the WEB-INF/lib
+ * directory.
*
* These mappings each have the type Map[String, File], where the key
* is the relative path within the .war file (e.g.
@@ -28,13 +28,13 @@ object WebappComponentsPlugin extends AutoPlugin {
object autoImport {
- lazy val webappResources: TaskKey[Map[String, File]] =
+ lazy val warResources: TaskKey[Map[String, File]] =
taskKey[Map[String, File]]("webapp resources")
- lazy val webappClasses: TaskKey[Map[String, File]] =
+ lazy val warClasses: TaskKey[Map[String, File]] =
taskKey[Map[String, File]]("webapp classes")
- lazy val webappLib: TaskKey[Map[String, File]] =
+ lazy val warLib: TaskKey[Map[String, File]] =
taskKey[Map[String, File]]("webapp lib")
}
@@ -42,34 +42,34 @@ object WebappComponentsPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
- lazy val webappContents: Initialize[Task[Map[String, File]]] =
+ lazy val warContents: Initialize[Task[Map[String, File]]] =
Def.task {
- webappResources.value ++
- webappClasses.value ++
- webappLib.value
+ warResources.value ++
+ warClasses.value ++
+ warLib.value
}
override val projectSettings: Seq[Setting[_]] = {
- val webappResourcesTask: Initialize[Task[Map[String, File]]] =
+ val warResourcesTask: Initialize[Task[Map[String, File]]] =
(Compile / sourceDirectory)
.map(_ / "webapp")
.map(WebappComponents.getResources)
- val webappClassesTask: Initialize[Task[Map[String, File]]] =
+ val warClassesTask: Initialize[Task[Map[String, File]]] =
(Runtime / fullClasspath)
.map(_.files)
.map(WebappComponents.getClasses)
- val webappLibTask: Initialize[Task[Map[String, File]]] =
+ val warLibTask: Initialize[Task[Map[String, File]]] =
(Runtime / fullClasspath)
.map(_.files)
.map(WebappComponents.getLib)
Seq(
- webappResources := webappResourcesTask.value,
- webappClasses := webappClassesTask.value,
- webappLib := webappLibTask.value
+ warResources := warResourcesTask.value,
+ warClasses := warClassesTask.value,
+ warLib := warLibTask.value
)
}
}
diff --git a/src/main/scala/com/earldouglas/sbt/war/WebappComponentsRunnerPlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WebappComponentsRunnerPlugin.scala
deleted file mode 100644
index 96828efb..00000000
--- a/src/main/scala/com/earldouglas/sbt/war/WebappComponentsRunnerPlugin.scala
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.earldouglas.sbt.war
-
-import sbt.Def.Initialize
-import sbt.Def.settingKey
-import sbt.Keys._
-import sbt._
-
-import java.nio.file.Files
-import java.nio.file.Paths
-import java.util.concurrent.atomic.AtomicReference
-import scala.sys.process.{Process => ScalaProcess}
-
-/** Launches the webapp components managed by WebappComponentsPlugin.
- * Uses a forked JVM to run Tomcat via
- * com.earldouglas:webapp-components-runner.
- */
-object WebappComponentsRunnerPlugin extends AutoPlugin {
-
- object autoImport {
- lazy val Webapp = config("webapp").hide
- lazy val webappPort = settingKey[Int]("webapp container port")
- lazy val webappStart = taskKey[Unit]("start webapp container")
- lazy val webappJoin = taskKey[Unit]("join webapp container")
- lazy val webappStop = taskKey[Unit]("stop webapp container")
- lazy val webappForkOptions =
- taskKey[ForkOptions]("webapp container fork options")
- lazy val webappComponentsRunnerVersion =
- settingKey[String]("webapp-components-runner version")
- lazy val webappComponentsRunnerMainClass =
- settingKey[String]("webapp-components-runner main class")
- }
-
- import autoImport._
- import WebappRunnerPlugin.autoImport._
-
- override val requires: Plugins =
- WebappComponentsPlugin && WebappRunnerPlugin
-
- override val projectConfigurations: Seq[Configuration] =
- Seq(Webapp)
-
- private lazy val containerInstance =
- new AtomicReference[Option[ScalaProcess]](None)
-
- override val projectSettings: Seq[Setting[_]] = {
-
- def stopContainerInstance(): Unit = {
- val oldProcess = containerInstance.getAndSet(None)
- oldProcess.foreach(_.destroy())
- }
-
- val runnerConfigFile: Initialize[Task[File]] =
- Def.task {
-
- val emptyDir: File = (Compile / target).value / "empty"
-
- val resourceMapString =
- WebappComponentsPlugin.webappContents.value
- .map { case (k, v) =>
- s"${k}->${v}"
- }
- .mkString(",")
-
- val configurationFile: File =
- (Compile / target).value / "webapp-components.properties"
-
- Files
- .writeString(
- Paths.get(configurationFile.getPath()),
- s"""|hostname=localhost
- |port=${webappPort.value}
- |contextPath=
- |emptyWebappDir=${emptyDir}
- |emptyClassesDir=${emptyDir}
- |resourceMap=${resourceMapString}
- |""".stripMargin
- )
- .toFile()
- }
-
- val startWebapp: Initialize[Task[Unit]] =
- Def.task {
- stopContainerInstance()
-
- val runnerJars: Seq[File] =
- Classpaths
- .managedJars(Webapp, classpathTypes.value, update.value)
- .map(_.data)
- .toList
-
- streams.value.log.info("[sbt-war] Starting server")
- val process: ScalaProcess =
- Fork.java.fork(
- webappForkOptions.value,
- Seq(
- "-cp",
- Path.makeString(runnerJars),
- webappComponentsRunnerMainClass.value,
- runnerConfigFile.value.getPath()
- )
- )
- containerInstance.set(Some(process))
- }
-
- val joinWebapp: Initialize[Task[Unit]] =
- Def.task(containerInstance.get.map(_.exitValue()))
-
- val stopWebapp: Initialize[Task[Unit]] =
- Def.task(stopContainerInstance())
-
- val forkOptions: Initialize[Task[ForkOptions]] =
- Def.task {
- ForkOptions()
- .withOutputStrategy(Some(BufferedOutput(streams.value.log)))
- }
-
- val onLoadSetting: Initialize[State => State] =
- Def.setting {
- (Global / onLoad).value
- .compose { state: State =>
- state.addExitHook(stopContainerInstance())
- }
- }
-
- val runnerLibraries: Initialize[Seq[ModuleID]] =
- Def.setting {
- Seq(
- "com.earldouglas" % "webapp-components-runner" % webappComponentsRunnerVersion.value % Webapp,
- "com.heroku" % "webapp-runner" % webappRunnerVersion.value % Webapp
- )
- }
-
- Seq(
- webappPort := 8080,
- webappStart := startWebapp.value,
- webappJoin := joinWebapp.value,
- webappStop := stopWebapp.value,
- webappForkOptions := forkOptions.value,
- Global / onLoad := onLoadSetting.value,
- webappComponentsRunnerVersion := BuildInfo.webappComponentsRunnerVersion,
- libraryDependencies ++= runnerLibraries.value,
- webappComponentsRunnerMainClass := "com.earldouglas.WebappComponentsRunner"
- )
- }
-}
diff --git a/src/main/scala/com/earldouglas/sbt/war/WebappRunnerPlugin.scala b/src/main/scala/com/earldouglas/sbt/war/WebappRunnerPlugin.scala
deleted file mode 100644
index 4568d89d..00000000
--- a/src/main/scala/com/earldouglas/sbt/war/WebappRunnerPlugin.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.earldouglas.sbt.war
-import sbt.Def.settingKey
-import sbt._
-
-/** Launches a webapp composed of in-place resources, classes, and
- * libraries.
- */
-object WebappRunnerPlugin extends AutoPlugin {
-
- object autoImport {
- lazy val webappRunnerVersion =
- settingKey[String]("webapp-runner version")
- }
-
- import autoImport._
-
- override lazy val projectSettings =
- Seq(
- webappRunnerVersion := BuildInfo.webappRunnerVersion
- )
-}
diff --git a/src/sbt-test/plugins/sbt-war/sbt/test.sbt b/src/sbt-test/plugins/sbt-war/sbt/test.sbt
index f5207ce5..13a0546e 100644
--- a/src/sbt-test/plugins/sbt-war/sbt/test.sbt
+++ b/src/sbt-test/plugins/sbt-war/sbt/test.sbt
@@ -1,7 +1,6 @@
enablePlugins(SbtWar)
-webappPort := 8083
-warPort := 8085
+warPort := 8081
TaskKey[Unit]("await-open") := {
@@ -27,7 +26,6 @@ TaskKey[Unit]("await-open") := {
}
}
- awaitOpen(webappPort.value)
awaitOpen(warPort.value)
}
@@ -55,7 +53,6 @@ TaskKey[Unit]("await-closed") := {
}
}
- awaitClosed(webappPort.value)
awaitClosed(warPort.value)
}
@@ -92,7 +89,7 @@ TaskKey[Unit]("check") := {
.openConnection
.asInstanceOf[HttpURLConnection]
- val name: String = s"WarRunnerPlugin: GET ${url}"
+ val name: String = s"SbtWar: GET ${url}"
c.setInstanceFollowRedirects(false)
c.setRequestMethod("GET")
@@ -133,53 +130,47 @@ TaskKey[Unit]("check") := {
}
}
- def check(port: Int): Unit = {
-
- assertEquals(
- url = s"http://localhost:${port}/",
- expectedBody = Source
- .fromFile((Compile / sourceDirectory).value / "webapp" / "index.html")
- .mkString
- )
-
- assertEquals(
- url = s"http://localhost:${port}/count",
- expectedBody = """|{
- | "count": 1
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${port}/count",
- expectedBody = """|{
- | "count": 2
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${port}/count",
- expectedBody = """|{
- | "count": 3
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${port}/count",
- expectedBody = """|{
- | "count": 4
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${port}/hello",
- expectedBody = """Hello, world!
"""
- )
- }
+ assertEquals(
+ url = s"http://localhost:${warPort.value}/",
+ expectedBody = Source
+ .fromFile((Compile / sourceDirectory).value / "webapp" / "index.html")
+ .mkString
+ )
+
+ assertEquals(
+ url = s"http://localhost:${warPort.value}/count",
+ expectedBody = """|{
+ | "count": 1
+ |}
+ |""".stripMargin
+ )
+
+ assertEquals(
+ url = s"http://localhost:${warPort.value}/count",
+ expectedBody = """|{
+ | "count": 2
+ |}
+ |""".stripMargin
+ )
- check(webappPort.value)
- check(warPort.value)
+ assertEquals(
+ url = s"http://localhost:${warPort.value}/count",
+ expectedBody = """|{
+ | "count": 3
+ |}
+ |""".stripMargin
+ )
+
+ assertEquals(
+ url = s"http://localhost:${warPort.value}/count",
+ expectedBody = """|{
+ | "count": 4
+ |}
+ |""".stripMargin
+ )
+
+ assertEquals(
+ url = s"http://localhost:${warPort.value}/hello",
+ expectedBody = """Hello, world!
"""
+ )
}
diff --git a/src/sbt-test/plugins/sbt-war/test b/src/sbt-test/plugins/sbt-war/test
index bb9a24a1..2f6d91a6 100644
--- a/src/sbt-test/plugins/sbt-war/test
+++ b/src/sbt-test/plugins/sbt-war/test
@@ -1,10 +1,14 @@
> setup
> reload
-> webappStart
+> warQuickstart
+> awaitOpen
+> check
+> warStop
+> awaitClosed
+
> warStart
> awaitOpen
> check
-> webappStop
> warStop
> awaitClosed
diff --git a/src/sbt-test/plugins/war-package-runner/sbt/test.sbt b/src/sbt-test/plugins/war-package-runner/sbt/test.sbt
deleted file mode 100644
index ad5e4dba..00000000
--- a/src/sbt-test/plugins/war-package-runner/sbt/test.sbt
+++ /dev/null
@@ -1,176 +0,0 @@
-enablePlugins(WarPackageRunnerPlugin)
-
-warPort := 8081
-
-TaskKey[Unit]("await-open") := {
-
- def isOpen(port: Int): Boolean =
- try {
- import java.net.Socket
- import java.net.InetSocketAddress
- val socket: Socket = new Socket()
- socket.connect(new InetSocketAddress("localhost", port))
- socket.close()
- true
- } catch {
- case e: Exception => false
- }
-
- def awaitOpen(port: Int, retries: Int = 40): Unit =
- if (!isOpen(port)) {
- if (retries > 0) {
- Thread.sleep(250)
- awaitOpen(port, retries - 1)
- } else {
- throw new Exception(s"expected port $port to be open")
- }
- }
-
- awaitOpen(warPort.value)
-}
-
-TaskKey[Unit]("await-closed") := {
-
- def isOpen(port: Int): Boolean =
- try {
- import java.net.Socket
- import java.net.InetSocketAddress
- val socket: Socket = new Socket()
- socket.connect(new InetSocketAddress("localhost", port))
- socket.close()
- true
- } catch {
- case e: Exception => false
- }
-
- def awaitClosed(port: Int, retries: Int = 40): Unit =
- if (isOpen(port)) {
- if (retries > 0) {
- Thread.sleep(250)
- awaitClosed(port, retries - 1)
- } else {
- throw new Exception(s"expected port $port to be closed")
- }
- }
-
- awaitClosed(warPort.value)
-}
-
-TaskKey[Unit]("check") := {
-
- import java.net.HttpURLConnection
- import java.net.URI
- import java.net.URL
- import javax.net.ssl.HostnameVerifier
- import javax.net.ssl.HttpsURLConnection
- import javax.net.ssl.SSLSession
- import scala.io.Source
-
- val log: sbt.internal.util.ManagedLogger = streams.value.log
-
- HttpsURLConnection.setDefaultHostnameVerifier(
- new HostnameVerifier() {
- override def verify(
- hostname: String,
- sslSession: SSLSession
- ): Boolean = {
- hostname == "localhost"
- }
- }
- )
-
- def assertEquals(
- url: String,
- expectedBody: String
- ): Unit = {
- val c: HttpURLConnection =
- (new URI(url)
- .toURL())
- .openConnection
- .asInstanceOf[HttpURLConnection]
-
- val name: String = s"WarRunnerPlugin: GET ${url}"
-
- c.setInstanceFollowRedirects(false)
- c.setRequestMethod("GET")
- c.setDoOutput(false)
-
- val obtainedStatus: Int =
- c.getResponseCode()
- val obtainedBody: String =
- Source.fromInputStream(c.getInputStream()).mkString
-
- val expectedStatus: Int = 200
-
- val statusMatch: Boolean =
- expectedStatus == obtainedStatus
- val bodyMatch: Boolean =
- expectedBody == obtainedBody
-
- if (!statusMatch || !bodyMatch) {
- log.error(name)
- sys.error(
- s"""|${name}:
- | expected:
- | * status: ${expectedStatus}
- | * body:
- | > ${expectedBody
- .toString()
- .replaceAll("\n", "\n > ")}
- | obtained:
- | * status: ${obtainedStatus}
- | * body:
- | > ${obtainedBody
- .toString()
- .replaceAll("\n", "\n > ")}
- |""".stripMargin
- )
- } else {
- log.success(name)
- }
- }
-
- assertEquals(
- url = s"http://localhost:${warPort.value}/",
- expectedBody = Source
- .fromFile((Compile / sourceDirectory).value / "webapp" / "index.html")
- .mkString
- )
-
- assertEquals(
- url = s"http://localhost:${warPort.value}/count",
- expectedBody = """|{
- | "count": 1
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${warPort.value}/count",
- expectedBody = """|{
- | "count": 2
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${warPort.value}/count",
- expectedBody = """|{
- | "count": 3
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${warPort.value}/count",
- expectedBody = """|{
- | "count": 4
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${warPort.value}/hello",
- expectedBody = """Hello, world!
"""
- )
-}
diff --git a/src/sbt-test/plugins/war-package-runner/template.sbt b/src/sbt-test/plugins/war-package-runner/template.sbt
deleted file mode 100644
index 4cecf3dc..00000000
--- a/src/sbt-test/plugins/war-package-runner/template.sbt
+++ /dev/null
@@ -1,10 +0,0 @@
-TaskKey[Unit]("setup") := {
- sbt.io.IO.copyDirectory(
- new java.io.File(sys.props("templateDirectory")),
- baseDirectory.value
- )
- sbt.io.IO.copyFile(
- new java.io.File("sbt", "test.sbt"),
- new java.io.File("test.sbt")
- )
-}
diff --git a/src/sbt-test/plugins/war-package-runner/test b/src/sbt-test/plugins/war-package-runner/test
deleted file mode 100644
index f0afc7bc..00000000
--- a/src/sbt-test/plugins/war-package-runner/test
+++ /dev/null
@@ -1,8 +0,0 @@
-> setup
-> reload
-
-> warStart
-> awaitOpen
-> check
-> warStop
-> awaitClosed
diff --git a/src/sbt-test/plugins/webapp-components-runner/sbt/test.sbt b/src/sbt-test/plugins/webapp-components-runner/sbt/test.sbt
deleted file mode 100644
index 1fa16e53..00000000
--- a/src/sbt-test/plugins/webapp-components-runner/sbt/test.sbt
+++ /dev/null
@@ -1,176 +0,0 @@
-enablePlugins(WebappComponentsRunnerPlugin)
-
-webappPort := 8082
-
-TaskKey[Unit]("await-open") := {
-
- def isOpen(port: Int): Boolean =
- try {
- import java.net.Socket
- import java.net.InetSocketAddress
- val socket: Socket = new Socket()
- socket.connect(new InetSocketAddress("localhost", port))
- socket.close()
- true
- } catch {
- case e: Exception => false
- }
-
- def awaitOpen(port: Int, retries: Int = 40): Unit =
- if (!isOpen(port)) {
- if (retries > 0) {
- Thread.sleep(250)
- awaitOpen(port, retries - 1)
- } else {
- throw new Exception(s"expected port $port to be open")
- }
- }
-
- awaitOpen(webappPort.value)
-}
-
-TaskKey[Unit]("await-closed") := {
-
- def isOpen(port: Int): Boolean =
- try {
- import java.net.Socket
- import java.net.InetSocketAddress
- val socket: Socket = new Socket()
- socket.connect(new InetSocketAddress("localhost", port))
- socket.close()
- true
- } catch {
- case e: Exception => false
- }
-
- def awaitClosed(port: Int, retries: Int = 40): Unit =
- if (isOpen(port)) {
- if (retries > 0) {
- Thread.sleep(250)
- awaitClosed(port, retries - 1)
- } else {
- throw new Exception(s"expected port $port to be closed")
- }
- }
-
- awaitClosed(webappPort.value)
-}
-
-TaskKey[Unit]("check") := {
-
- import java.net.HttpURLConnection
- import java.net.URI
- import java.net.URL
- import javax.net.ssl.HostnameVerifier
- import javax.net.ssl.HttpsURLConnection
- import javax.net.ssl.SSLSession
- import scala.io.Source
-
- val log: sbt.internal.util.ManagedLogger = streams.value.log
-
- HttpsURLConnection.setDefaultHostnameVerifier(
- new HostnameVerifier() {
- override def verify(
- hostname: String,
- sslSession: SSLSession
- ): Boolean = {
- hostname == "localhost"
- }
- }
- )
-
- def assertEquals(
- url: String,
- expectedBody: String
- ): Unit = {
- val c: HttpURLConnection =
- (new URI(url)
- .toURL())
- .openConnection
- .asInstanceOf[HttpURLConnection]
-
- val name: String = s"WebappComponentsRunnerPlugin: GET ${url}"
-
- c.setInstanceFollowRedirects(false)
- c.setRequestMethod("GET")
- c.setDoOutput(false)
-
- val obtainedStatus: Int =
- c.getResponseCode()
- val obtainedBody: String =
- Source.fromInputStream(c.getInputStream()).mkString
-
- val expectedStatus: Int = 200
-
- val statusMatch: Boolean =
- expectedStatus == obtainedStatus
- val bodyMatch: Boolean =
- expectedBody == obtainedBody
-
- if (!statusMatch || !bodyMatch) {
- log.error(name)
- sys.error(
- s"""|${name}:
- | expected:
- | * status: ${expectedStatus}
- | * body:
- | > ${expectedBody
- .toString()
- .replaceAll("\n", "\n > ")}
- | obtained:
- | * status: ${obtainedStatus}
- | * body:
- | > ${obtainedBody
- .toString()
- .replaceAll("\n", "\n > ")}
- |""".stripMargin
- )
- } else {
- log.success(name)
- }
- }
-
- assertEquals(
- url = s"http://localhost:${webappPort.value}/",
- expectedBody = Source
- .fromFile((Compile / sourceDirectory).value / "webapp" / "index.html")
- .mkString
- )
-
- assertEquals(
- url = s"http://localhost:${webappPort.value}/count",
- expectedBody = """|{
- | "count": 1
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${webappPort.value}/count",
- expectedBody = """|{
- | "count": 2
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${webappPort.value}/count",
- expectedBody = """|{
- | "count": 3
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${webappPort.value}/count",
- expectedBody = """|{
- | "count": 4
- |}
- |""".stripMargin
- )
-
- assertEquals(
- url = s"http://localhost:${webappPort.value}/hello",
- expectedBody = """Hello, world!
"""
- )
-}
diff --git a/src/sbt-test/plugins/webapp-components-runner/template.sbt b/src/sbt-test/plugins/webapp-components-runner/template.sbt
deleted file mode 100644
index 4cecf3dc..00000000
--- a/src/sbt-test/plugins/webapp-components-runner/template.sbt
+++ /dev/null
@@ -1,10 +0,0 @@
-TaskKey[Unit]("setup") := {
- sbt.io.IO.copyDirectory(
- new java.io.File(sys.props("templateDirectory")),
- baseDirectory.value
- )
- sbt.io.IO.copyFile(
- new java.io.File("sbt", "test.sbt"),
- new java.io.File("test.sbt")
- )
-}
diff --git a/src/sbt-test/plugins/webapp-components-runner/test b/src/sbt-test/plugins/webapp-components-runner/test
deleted file mode 100644
index ed33006a..00000000
--- a/src/sbt-test/plugins/webapp-components-runner/test
+++ /dev/null
@@ -1,8 +0,0 @@
-> setup
-> reload
-
-> webappStart
-> awaitOpen
-> check
-> webappStop
-> awaitClosed
diff --git a/src/sbt-test/plugins/webapp-components/sbt/test.sbt b/src/sbt-test/plugins/webapp-components/sbt/test.sbt
index 13f70ac1..483e8a05 100644
--- a/src/sbt-test/plugins/webapp-components/sbt/test.sbt
+++ b/src/sbt-test/plugins/webapp-components/sbt/test.sbt
@@ -75,7 +75,7 @@ val checkClasses: Def.Initialize[Task[Unit]] =
.map(x => s"WEB-INF/classes/${x}" -> root / x)
.toMap
},
- obtained = webappClasses.value
+ obtained = warClasses.value
)
}
@@ -128,7 +128,7 @@ val checkLib: Def.Initialize[Task[Unit]] =
assertContains(
name = "WebappComponentsPlugin: checkLib",
expected = expected.map(x => s"WEB-INF/lib/${x}"),
- obtained = webappLib.value
+ obtained = warLib.value
)
}
@@ -178,6 +178,6 @@ lazy val checkResources: Def.Initialize[Task[Unit]] =
.map(x => x -> root / "webapp" / x)
.toMap
},
- obtained = webappResources.value
+ obtained = warResources.value
)
}