diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4ff4b1f..a8be88e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,6 @@ name: build -on: - push: - paths-ignore: - - '.github/workflows/examples-**' - - 'examples/**' +on: [push, pull_request] jobs: build: diff --git a/.github/workflows/examples-adder.yml b/.github/workflows/examples-adder.yml deleted file mode 100644 index dcf59472..00000000 --- a/.github/workflows/examples-adder.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-adder - -on: - push: - paths: - - '.github/workflows/examples-adder.yml' - - 'examples/adder/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 11 - - run: | - cd examples/adder - sbt Jetty/test diff --git a/.github/workflows/examples-async.yml b/.github/workflows/examples-async.yml deleted file mode 100644 index 43e80797..00000000 --- a/.github/workflows/examples-async.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-async - -on: - push: - paths: - - '.github/workflows/examples-async.yml' - - 'examples/async/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/async - sbt Jetty/test diff --git a/.github/workflows/examples-free.yml b/.github/workflows/examples-free.yml deleted file mode 100644 index fe56dc35..00000000 --- a/.github/workflows/examples-free.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-free - -on: - push: - paths: - - '.github/workflows/examples-free.yml' - - 'examples/free/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/free - sbt Jetty/test diff --git a/.github/workflows/examples-frege.yml b/.github/workflows/examples-frege.yml deleted file mode 100644 index 80f4db84..00000000 --- a/.github/workflows/examples-frege.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-frege - -on: - push: - paths: - - '.github/workflows/examples-frege.yml' - - 'examples/frege/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/frege - sbt Jetty/test diff --git a/.github/workflows/examples-getting-started.yml b/.github/workflows/examples-getting-started.yml deleted file mode 100644 index 33fc5330..00000000 --- a/.github/workflows/examples-getting-started.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-getting-started - -on: - push: - paths: - - '.github/workflows/examples-getting-started.yml' - - 'examples/getting-started/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/getting-started - sbt Jetty/test diff --git a/.github/workflows/examples-http4s.yml b/.github/workflows/examples-http4s.yml deleted file mode 100644 index ea1843b5..00000000 --- a/.github/workflows/examples-http4s.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: examples-http4s -on: - push: - paths: - - '.github/workflows/examples-http4s.yml' - - 'examples/http4s/**' -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v18 - with: - nix_path: nixpkgs=channel:nixos-22.11 - - uses: workflow/nix-shell-action@v3 - with: - script: | - cd examples/http4s - nix-shell --run 'sbt "Jetty / test"' diff --git a/.github/workflows/examples-https.yml b/.github/workflows/examples-https.yml deleted file mode 100644 index 6bf73e6b..00000000 --- a/.github/workflows/examples-https.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-https - -on: - push: - paths: - - '.github/workflows/examples-https.yml' - - 'examples/https/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/https - sbt Tomcat/test diff --git a/.github/workflows/examples-jetty-11.yml b/.github/workflows/examples-jetty-11.yml deleted file mode 100644 index a6da3d32..00000000 --- a/.github/workflows/examples-jetty-11.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: examples-jetty-11 -on: - push: - paths: - - '.github/workflows/examples-jetty-11.yml' - - 'examples/jetty-11/**' -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v18 - with: - nix_path: nixpkgs=channel:nixos-22.11 - - uses: workflow/nix-shell-action@v3 - with: - script: | - cd examples/jetty-11 - nix-shell --run 'sbt "Jetty / test"' diff --git a/.github/workflows/examples-lift.yml b/.github/workflows/examples-lift.yml deleted file mode 100644 index 322c6a02..00000000 --- a/.github/workflows/examples-lift.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-lift - -on: - push: - paths: - - '.github/workflows/examples-lift.yml' - - 'examples/lift/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/lift - sbt Jetty/test diff --git a/.github/workflows/examples-mustache.yml b/.github/workflows/examples-mustache.yml deleted file mode 100644 index 09a3b5ec..00000000 --- a/.github/workflows/examples-mustache.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-mustache - -on: - push: - paths: - - '.github/workflows/examples-mustache.yml' - - 'examples/mustache/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/mustache - sbt Jetty/test diff --git a/.github/workflows/examples-payara-micro.yml b/.github/workflows/examples-payara-micro.yml deleted file mode 100644 index dc18a91a..00000000 --- a/.github/workflows/examples-payara-micro.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-payara-micro - -on: - push: - paths: - - '.github/workflows/examples-payara-micro.yml' - - 'examples/payara-micro/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 11 - - run: | - cd examples/payara-micro - sbt Container/test diff --git a/.github/workflows/examples-sbt-0.x.yml b/.github/workflows/examples-sbt-0.x.yml deleted file mode 100644 index ee991cc7..00000000 --- a/.github/workflows/examples-sbt-0.x.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-sbt-0.x - -on: - push: - paths: - - '.github/workflows/examples-sbt-0.x.yml' - - 'examples/sbt-0.x/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/sbt-0.x - sbt jetty:test diff --git a/.github/workflows/examples-sbt-1.x.yml b/.github/workflows/examples-sbt-1.x.yml deleted file mode 100644 index 13569452..00000000 --- a/.github/workflows/examples-sbt-1.x.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-sbt-1.x - -on: - push: - paths: - - '.github/workflows/examples-sbt-1.x.yml' - - 'examples/sbt-1.x/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/sbt-1.x - sbt Jetty/test diff --git a/.github/workflows/examples-scala-js.yml b/.github/workflows/examples-scala-js.yml deleted file mode 100644 index f0d8c1a8..00000000 --- a/.github/workflows/examples-scala-js.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-scala-js - -on: - push: - paths: - - '.github/workflows/examples-scala-js.yml' - - 'examples/scala-js/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/scala-js - sbt fastOptJS Jetty/test diff --git a/.github/workflows/examples-scalatra.yml b/.github/workflows/examples-scalatra.yml deleted file mode 100644 index 396609af..00000000 --- a/.github/workflows/examples-scalatra.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-scalatra - -on: - push: - paths: - - '.github/workflows/examples-scalatra.yml' - - 'examples/scalatra/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/scalatra - sbt Jetty/test diff --git a/.github/workflows/examples-spray.yml b/.github/workflows/examples-spray.yml deleted file mode 100644 index 2f979d19..00000000 --- a/.github/workflows/examples-spray.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-spray - -on: - push: - paths: - - '.github/workflows/examples-spray.yml' - - 'examples/spray/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - run: | - cd examples/spray - sbt Jetty/test diff --git a/.github/workflows/examples-zio.yml b/.github/workflows/examples-zio.yml deleted file mode 100644 index 525b2ac2..00000000 --- a/.github/workflows/examples-zio.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: examples-zio - -on: - push: - paths: - - '.github/workflows/examples-zio.yml' - - 'examples/zio/**' - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-java@v1 - with: - java-version: 11 - - run: | - cd examples/zio - sbt Jetty/test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b295c6c..4a05675f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,55 +1,76 @@ # Contributing -## Testing +## Architecture -Publish to the local Maven repository: +```mermaid +flowchart TB -``` -$ sbt -> set version := "4.2.5" -> ^publishM2 -``` + subgraph webapp + WebappComponentsPlugin + WebappComponentsRunnerPlugin + webappRunnerCA[components-aware webapp-runner] + end -Update the versions in plugins.sbt: + subgraph war + WarPackagePlugin + WarPackageRunnerPlugin + webappRunner[webapp-runner] + end -``` -$ find examples -type f -name plugins.sbt -exec sed -i 's/4.2.4/4.2.5/' {} \; + SbtWar + + subgraph project + resources + classes + lib + end + + + WebappComponentsPlugin-->resources + WebappComponentsPlugin-->classes + WebappComponentsPlugin-->lib + + WebappComponentsRunnerPlugin-->WebappComponentsPlugin + WebappComponentsRunnerPlugin-->webappRunnerCA + + WarPackagePlugin-->WebappComponentsPlugin + + WarPackageRunnerPlugin-->WarPackagePlugin + WarPackageRunnerPlugin-->webappRunner + + SbtWar-->WebappComponentsRunnerPlugin + SbtWar-->WarPackageRunnerPlugin ``` -Run the tests for each: + +## Testing ``` -$ for i in examples/* - do - pushd $i - grep -q JettyPlugin build.sbt && sbt Jetty/test - grep -q TomcatPlugin build.sbt && sbt Tomcat/test - grep -q ContainerPlugin build.sbt && sbt Container/test - popd - done +$ sbt test scripted ``` ## Publishing -xsbt-web-plugin uses the process outlined in the [Using Sonatype][1] +sbt-war uses the process outlined in the [Using +Sonatype](https://www.scala-sbt.org/release/docs/Using-Sonatype.html) section of the sbt manual for publishing to Maven Central via Sonatype. -[1]: https://www.scala-sbt.org/release/docs/Using-Sonatype.html - Create a staging release in Sonatype: ``` +$ nix-shell $ sbt > set version := "4.2.5" -> ^publishSigned +> publishSigned ``` Review it: -* Go to [Staging Repositories][2] on Nexus Repository Manager +* Go to [Staging + Repositories](https://oss.sonatype.org/#stagingRepositories) on Nexus + Repository Manager * Review the contents of the staging repository -[2]: https://oss.sonatype.org/#stagingRepositories Release it: @@ -58,8 +79,7 @@ Release it: Wait for it to be synced to Maven Central: -* -* +* Update the documentation: @@ -78,6 +98,5 @@ $ git tag 4.2.5 $ git push --tags origin ``` -Update the [Giter8 template][3] to use the new version. - -[3]: https://github.com/earldouglas/xsbt-web-plugin.g8 +Update the [Giter8 template](https://github.com/earldouglas/xsbt-web-plugin.g8) +to use the new version. diff --git a/README.md b/README.md index de0624c0..607a9c14 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,47 @@ [![Build status](https://github.com/earldouglas/xsbt-web-plugin/workflows/build/badge.svg)](https://github.com/earldouglas/xsbt-web-plugin/actions) [![Latest version](https://img.shields.io/github/tag/earldouglas/xsbt-web-plugin.svg)](https://index.scala-lang.org/earldouglas/xsbt-web-plugin) -# xsbt-web-plugin +# sbt-war -xsbt-web-plugin is an [sbt](https://www.scala-sbt.org/) plugin for -building web applications with [Java -servlets](https://en.wikipedia.org/wiki/Java_servlet). +sbt-war is an [sbt](https://www.scala-sbt.org/) plugin for building Web +apps with [servlets](https://en.wikipedia.org/wiki/Java_servlet). -## Features - -* Package a project as a *.war* file -* Test and run under Jetty or Tomcat -* Deploy directly to Heroku or AWS -* Supports sbt 0.13.6 and up -* Supports Scala 2.10.2 and up - -## Getting help - -* Look for *earldouglas* in the `#sbt` channel on the [Scala](https://discord.com/invite/scala) Discord server -* Use the [*xsbt-web-plugin* tag](https://stackoverflow.com/questions/tagged/xsbt-web-plugin) on Stack Overflow -* Submit a bug report or feature request as a [new GitHub issue](https://github.com/earldouglas/xsbt-web-plugin/issues/new) -* See examples in the [examples/](examples/) directory - -## Quick reference - -Add xsbt-web-plugin to *project/plugins.sbt*: +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 [xsbt-web-plugin +v4.2.4](https://github.com/earldouglas/xsbt-web-plugin/tree/4.2.4). -```scala -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") -``` - -Enable the Jetty plugin: - -*build.sbt*: - -```scala -enablePlugins(JettyPlugin) -``` - -From the sbt console: +## Features -* Start (or restart) the container with `jetty:start` -* Stop the container with `jetty:stop` -* Build a *.war* file with `package` +* Package your project as a *.war* file +* Run your project in a Tomcat container -To use Tomcat instead of Jetty: +## Requirements -* Substitute `TomcatPlugin` for `JettyPlugin` -* Substitute `tomcat:start` for `jetty:start` -* Substitute `tomcat:stop` for `jetty:stop` +* sbt 1.x and up +* Scala 2.12.x and up -## Starting with Giter8 +## Getting help -``` -sbt new earldouglas/xsbt-web-plugin.g8 -``` +* Submit a question, bug report, or feature request as a [new GitHub + issue](https://github.com/earldouglas/xsbt-web-plugin/issues/new) +* Look for *earldouglas* in the `#sbt` channel on the [Scala Discord + server](https://discord.com/invite/scala) -## Starting from scratch +## Usage Create a new empty project: ``` -mkdir myproject -cd myproject +$ mkdir myproject +$ cd myproject ``` Set up the project structure: ``` -mkdir project -mkdir -p src/main/scala/mypackage +$ mkdir project +$ mkdir -p src/main/scala/mypackage ``` Configure sbt: @@ -77,21 +49,21 @@ Configure sbt: *project/build.properties:* ``` -sbt.version=1.6.2 +sbt.version=1.10.2 ``` *project/plugins.sbt:* ```scala -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") +addSbtPlugin("com.earldouglas" % "sbt-war" % "5.0.0-M1") ``` *build.sbt:* ```scala -scalaVersion := "3.0.1" -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided" -enablePlugins(TomcatPlugin) +scalaVersion := "3.5.1" +libraryDependencies += "jakarta.servlet" % "jakarta.servlet-api" % "6.0.0" % Provided +enablePlugins(SbtWar) ``` Add a servlet: @@ -101,24 +73,27 @@ Add a servlet: ```scala package mypackage -import javax.servlet.annotation.WebServlet -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse +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 = + override def doGet( + req: HttpServletRequest, + res: HttpServletResponse + ): Unit = res.setContentType("text/html") res.setCharacterEncoding("UTF-8") res.getWriter.write("""

Hello, world!

""") ``` -Run it with `tomcat:start`: +Run it from sbt with `warStart`: ``` $ sbt -> tomcat:start +> warStart ``` ``` @@ -126,549 +101,209 @@ $ curl localhost:8080/hello

Hello, world!

``` -## Configuration and use - -### Triggered execution - -xsbt-web-plugin supports sbt's [triggered -execution](http://www.scala-sbt.org/1.0/docs/Triggered-Execution.html) -by prefixing commands with `~`. - -*sbt console:* +Stop it with `warStop`: ``` -> ~jetty:start +> warStop ``` -This starts the Jetty container, then monitors the sources, resources, -and webapp directories for changes, which triggers a container restart. - -### Testing - -To run a projects tests against a running instance of the webapp, use -`:quicktest` or `:test`: +Create a .war file with `package`: ``` -> ~jetty:quicktest +> package ``` -### Container arguments +## Settings and commands -To pass extra arguments to the Jetty or Tomcat container, set -`containerArgs`: +| Setting 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 | +| `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` | +| `warForkOptions` | `ForkOptions` | `ForkOptions()` | Options for the forked JVM used when running with `warStart` | -```scala -containerArgs := Seq("--path", "/myservice") -``` - -* For available Jetty arguments, see the [Jetty Runner - docs](https://www.eclipse.org/jetty/documentation/current/runner.html#_full_configuration_reference) -* For available Tomcat arguments, see [webapp-runner#options](https://github.com/heroku/webapp-runner#options) - -### Custom container - -To use a custom J2EE container, e.g. a main class named `runner.Run`, -enable `ContainerPlugin` and set `containerLibs` and -`containerLaunchCmd`: - -```scala -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 -``` - -### Forked JVM options - -To set system properties for the forked container JVM, set -`containerForkOptions`: - -```scala -containerForkOptions := new ForkOptions(runJVMOptions = Seq("-Dh2g2=42")) -``` +| Task Key | Notes | +| ------------- | ----------------------------------------------------------------------- | +| `warStart` | Starts a local container, serving content from the packaged .war file | +| `warJoin` | Blocks until the container shuts down | +| `warStop` | Shuts down the container | +| `webappStart` | Starts a local container, serving content directly from project sources | +| `webappJoin` | Blocks until the container shuts down | +| `webappStop` | Shuts down the container | -Alternatively, set `javaOptions` in the `Jetty` (or `Tomcat`) -configuration: +### `war` vs. `webapp` -```scala -javaOptions in Jetty += "-Dh2g2=42" -``` - -To attach a debugger, set `-Xdebug` and `-Xrunjdwp`: +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. -*build.sbt:* +Use the `webapp` prefix in place of `war` to skip packaging, and run the +container directly from source: -```scala -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 - -### Debug mode - -To enable debugging through -[JDWP](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/introclientissues005.html), -use `jetty:debug` or `tomcat:debug`. Optionally set `debugAddress`, -which defaults to `"debug"` under Windows and `"8888"` otherwise, and -`debugOptions`, which defaults to: - -```scala -port => - Seq( "-Xdebug" - , Seq( "-Xrunjdwp:transport=dt_socket" - , "address=" + port - , "server=y" - , "suspend=n" - ).mkString(",") - ) +> webappStart ``` -### Jetty version +### `webappResources` -By default, [Jetty -Runner](https://www.eclipse.org/jetty/documentation/current/runner.html) -9.4.42 is used. To use a different version, set -`containerLibs`: +Webapp resources are the various static files, deployment descriptors, +etc. that go into a .war file. -```scala -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: +The `webappResources` 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. -```scala -containerMain := "org.mortbay.jetty.runner.Runner" -``` +By default, everything in *src/main/webapp* is included. -### Container port +For example, given the following .war file: -By default, the container runs on port *8080*. To use a different port, -set `containerPort`: - -```scala -containerPort := 9090 ``` - -### *jetty.xml* - -To use a *jetty.xml* configuration file, set `--config` in -`containerArgs`: - -```scala -containerArgs := Seq("--config", "/path/to/jetty.xml") +myproject.war +├── index.html +├── styles/ +│ └── theme.css +├── WEB-INF/ +│ └── web.xml +└── META-INF/ + └── MANIFEST.MF ``` -This option can be used to enable SSL and HTTPS. +The `webappResources` mapping would look like this: -### Tomcat version - -By default, [Webapp Runner](https://github.com/heroku/webapp-runner) -9.0.41.0 is used. To use a different version, set `containerLibs`: -41 -```scala -containerLibs in Tomcat := Seq("com.heroku" % "webapp-runner" % "8.5.61.0" intransitive()) ``` - -Depending on the version, it may also be necessary to specify the name -of Tomcat's runner: - -```scala -containerMain in Tomcat := "webapp.runner.launch.Main" +"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") ``` -### Extra container libraries - -Tomcat's webapp-runner does not ship with all of the libraries that can -be found in a complete Tomcat installation. To include extras, use -`containerLibs in Tomcat`: +To use a different directory, e.g. *src/main/WebContent*: ```scala -containerLibs in Tomcat += "org.apache.tomcat" % "tomcat-jdbc" % "8.5.15" +webappResources := + (Compile / sourceDirectory) + .map(_ / "WebContent") + .map(WebappComponents.getResources) ``` -### Renaming the *.war* file - -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. +Manifest attributes of the *.war* file can be configured via +`packageOptions`: ```scala -artifactName := { (v: ScalaVersion, m: ModuleID, a: Artifact) => - a.name + "." + a.extension -} +sbt.Keys.`package` / packageOptions += + Package.ManifestAttributes( + java.util.jar.Attributes.Name.SEALED -> "true" + ) ``` -See ["Modifying default -artifacts"](http://www.scala-sbt.org/1.0/docs/Artifacts.html#Modifying+default+artifacts) -in the sbt documentation for additional information. -### Massaging the *.war* file +### `webappClasses` -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: - -```scala -webappPostProcess := { - val log = streams.value.log - webappDir: File => - def listFiles(level: Int)(f: File): Unit = { - val indent = ((1 until level) map { _ => " " }).mkString - if (f.isDirectory) { - log.info(indent + f.getName + "/") - f.listFiles foreach { listFiles(level + 1) } - } else log.info(indent + f.getName) - } - listFiles(1)(webappDir) -} -``` - -To include webapp resources from multiple directories in the prepared -*webapp* directory: +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`: ```scala -webappPostProcess := { - webappDir: File => - val baseDir = baseDirectory.value / "src" / "main" - IO.copyDirectory(baseDir / "webapp1", webappDir) - IO.copyDirectory(baseDir / "webapp2", webappDir) - IO.copyDirectory(baseDir / "webapp3", webappDir) -} +exportJars := true ``` -### Custom resources directory +See ["Configure +packaging"](https://www.scala-sbt.org/1.x/docs/Howto-Package.html) in +the sbt documentation for additional information. -Files in the extra resource directory are not compiled, and are bundled -directly in the project artifact *.jar* file. +### `webappLib` -To add a custom resources directory, set `unmanagedResourceDirectories`: +By default, all runtime dependencies are copied into the *WEB-INF/lib* +directory. -```scala -unmanagedResourceDirectories in Compile += (sourceDirectory in Compile).value / "extra" -``` - -### Custom sources directory - -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`: +To use a dependency at compile time but exclude it from the .war file, +set its scope to `Provided`: ```scala -unmanagedSourceDirectories in Compile += (sourceDirectory in Compile).value / "extra" +libraryDependencies += "foo" % "bar" % "1.0.0" % Provided ``` -### Utilizing *WEB-INF/classes* +### `webappRunnerVersion` -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`: +By default, [Webapp Runner](https://github.com/heroku/webapp-runner) +10.1.x is used. To use a different version, set `War / +webappRunnerVersion`: ```scala -webappWebInfClasses := true +webappRunnerVersion := "9.0.93.0" ``` -### Web application destination +### `warPort` and `webappPort` -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: +By default, the container runs on port *8080*. To use a different port, +set `warPort`/`webappPort`: ```scala -target in webappPrepare := target.value / "WebContent" +warPort := 9090 ``` -### Web application resources - -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: - ```scala -sourceDirectory in webappPrepare := (sourceDirectory in Compile).value / "WebContent" +webappPort := 9090 ``` -### Prepare the web application for execution and deployment - -For situations when the prepared */target/webapp* directory is -needed, but the packaged *.war* file isn't. +### `warForkOptions` -*sbt console:* +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 `warForkOptions`. -``` -webappPrepare -``` - -### Add manifest attributes +For example: to attach a debugger, set `-Xdebug` and `-Xrunjdwp`: -Manifest attributes of the *.war* file can be configured via -`packageOptions in sbt.Keys.package` in *build.sbt*: - -```scala -packageOptions in sbt.Keys.`package` += - Package.ManifestAttributes( java.util.jar.Attributes.Name.SEALED -> "true" ) -``` - -### Inherit manifest attributes - -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`: +*build.sbt:* ```scala -inheritJarManifest := true +warForkOptions := + ForkOptions() + .withRunJVMOptions( + Seq( + "-Xdebug", + "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000" + ) + ) ``` -### Container shutdown and sbt +### `warStart` and `webappStart` -By default, sbt will shutdown the running container when exiting sbt. - -To allow the container to continue running after sbt exits, set -`containerShutdownOnExit`: - -```scala -containerShutdownOnExit := false ``` - -### Deploying to Heroku - -Enable the `HerokuDeploy` plugin and configure your app name: - -```scala -enablePlugins(HerokuDeploy) - -herokuAppName := "my-heroku-app" +> warStart ``` -Either install the [Heroku Toolbelt](https://toolbelt.heroku.com/), or -set your Heroku API key as an environment variable, launch sbt, and -deploy with `herokuDeploy`: - ``` -$ HEROKU_API_KEY="xxx-xxx-xxxx" sbt -> herokuDeploy +> webappStart ``` -Check out your deployed application at -`https://my-heroku-app.herokuapp.com`. +### `warJoin` and `webappJoin` -### Deploying to Elastic Beanstalk +To block sbt while the container is running, use `warJoin`/`webappJoin`: -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: - -```scala -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 +$ sbt warStart warJoin ``` -Check out your deployed application at -`http://my-elastic-beanstalk-app.us-west-1.elasticbeanstalk.com`. - -### Block sbt on running container - -To start the container from the command line and block sbt from exiting -prematurely, use `jetty:join`: - ``` -$ sbt jetty:start jetty:join +$ sbt webappStart webappJoin ``` This is useful for running sbt in production (e.g. in a Docker -container). +container), if you're into that kind of thing. -### Build and run from Docker +### `warStop` and `webappStop` -Choose a Docker image that has sbt installed, and use `sbt -:start :join`: - -Using [hseeberger/scala-sbt](https://hub.docker.com/r/hseeberger/scala-sbt/); - -``` -$ docker run -p 8080:8080 -v $PWD:/root hseeberger/scala-sbt sbt tomcat:start tomcat:join -``` - -Using [bigtruedata/sbt](https://hub.docker.com/r/bigtruedata/sbt/): - -``` -$ docker run -p 8080:8080 -v $PWD:/app bigtruedata/sbt sbt tomcat:start tomcat:join -``` - -Test it out: - -``` -$ curl localhost:8080/hello - - -

Hello, world!

- - -``` - -### Run a packaged *.war* file from Docker - -First, package a *.war* file: - -``` -$ sbt package -[info] Packaging .../target/scala-2.10/getting-started_2.10-0.1-SNAPSHOT.war . -``` - -Run it using [Jetty](https://hub.docker.com/_/jetty/): - -``` -$ docker run -p 8080:8080 -v /path/to/myproject/target/scala-2.10:/var/lib/jetty/webapps jetty -``` - -Run it using [Tomcat](https://hub.docker.com/_/tomcat/): +Stop the running container with `warStop`/`webappStop`: ``` -$ docker run -p 8080:8080 -v /path/to/myproject/target/scala-2.10:/usr/local/tomcat/webapps tomcat +> warStop ``` -Test it out: - -``` -$ curl localhost:8080/myproject_2.10-0.1-SNAPSHOT/hello - - -

Hello, world!

- - -``` - -### Build a Docker image - -Configure a Docker build with `sbt-native-packager`: - -*project/plugins.sbt:* - -```scala -addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.11") -``` - -*build.sbt:* - -```scala -enablePlugins(DockerPlugin) - -dockerBaseImage := "tomcat:9.0" - -Docker / defaultLinuxInstallLocation := "/usr/local/tomcat/webapps" - -dockerExposedVolumes := Seq((Docker / defaultLinuxInstallLocation).value) - -Docker / mappings += sbt.Keys.`package`.value -> "/usr/local/tomcat/webapps/ROOT.war" - -dockerEntrypoint := Seq("catalina.sh", "run") -``` - -Build the project from sbt as a Docker image: - -``` -> docker:publishLocal -``` - -Run it: - -``` -$ docker run -it --rm -p 8080:8080 my-web-project:0.1.0-SNAPSHOT -``` - -### Quickstart mode - -The development cycle can be sped up by serving static resources -directly from source, and avoiding packaging of compiled artifacts. - -Use `:quickstart` in place of `:start` to run the -container in quickstart mode: - -``` -> jetty:quickstart -``` - -### Running multiple containers - -To launch using more than a single container, set `containerScale`: - -```scala -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. - -### JRebel integration - -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 +> webappStop ``` diff --git a/build.sbt b/build.sbt index 4b5b28fb..7bbd0a61 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ // General enablePlugins(SbtPlugin) -name := "xsbt-web-plugin" +name := "sbt-war" organization := "com.earldouglas" sbtPlugin := true scalacOptions ++= Seq("-feature", "-deprecation") diff --git a/examples/adder/README.md b/examples/adder/README.md deleted file mode 100644 index bf52308c..00000000 --- a/examples/adder/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - get sum -[info] - add some numbers -[info] - post an unparseable number -[info] Run completed in 1 second, 428 milliseconds. -[info] Total number of tests run: 3 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 3, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 4 s, completed Nov 13, 2022 1:50:36 PM -[info] waiting for server to shut down... -``` diff --git a/examples/adder/build.sbt b/examples/adder/build.sbt deleted file mode 100644 index 3c7be000..00000000 --- a/examples/adder/build.sbt +++ /dev/null @@ -1,15 +0,0 @@ -libraryDependencies += "com.h2database" % "h2" % "2.3.232" -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(JettyPlugin) - -containerForkOptions := - ForkOptions().withEnvVars( - Map( - "DB_DRIVER" -> "org.h2.Driver", - "DB_URL" -> "jdbc:h2:mem:adder", - "DB_USER" -> "sa", - "DB_PASS" -> "" - ) - ) diff --git a/examples/adder/project/build.properties b/examples/adder/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/adder/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/adder/project/plugins.sbt b/examples/adder/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/adder/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/adder/src/main/scala/adder.scala b/examples/adder/src/main/scala/adder.scala deleted file mode 100644 index f53dea2a..00000000 --- a/examples/adder/src/main/scala/adder.scala +++ /dev/null @@ -1,71 +0,0 @@ -import scala.concurrent.{ExecutionContext => EC} - -object Adder { - - def init(implicit ec: EC): Service[Unit] = - Service.update("""|create table if not exists - | amounts ( id int not null auto_increment - | , amount int not null - | , primary key (id) - | ) - |""".stripMargin) >> - Service.update("""|create table if not exists - | sums ( last_id int not null unique - | , sum int not null - | ) - |""".stripMargin) - - def add(amount: Int)(implicit ec: EC): Service[Unit] = - Service.update( - "insert into amounts (amount) values (?)", - _.setInt(1, amount) - ) - - def getAmounts(afterId: Int)(implicit - ec: EC - ): Service[(Int, List[Int])] = - Service.query( - "select * from amounts where id > ? order by id asc", - { s => - s.setInt(1, afterId) - val r = s.executeQuery() - var amounts: List[Int] = Nil - var lastId: Int = afterId - while (r.next()) { - lastId = r.getInt("id") - amounts = r.getInt("amount") :: amounts - } - (lastId, amounts) - } - ) - - def getSum(implicit ec: EC): Service[(Int, Int)] = - Service.query( - "select last_id, sum from sums order by last_id desc limit 1", - { s => - val r = s.executeQuery() - if (r.next()) { - (r.getInt("last_id"), r.getInt("sum")) - } else { - (0, 0) - } - } - ) - - def update(implicit ec: EC): Service[Unit] = - for { - sum <- getSum - amt <- getAmounts(sum._1) - _ <- Service.update( - "insert into sums (last_id, sum) values (?, ?)", - { s => - s.setInt(1, amt._1) - s.setInt(2, sum._2 + amt._2.sum) - } - ) - _ <- Service.update( - "delete from sums where last_id < ?", - _.setInt(1, amt._1) - ) - } yield () -} diff --git a/examples/adder/src/main/scala/service.scala b/examples/adder/src/main/scala/service.scala deleted file mode 100644 index f358023d..00000000 --- a/examples/adder/src/main/scala/service.scala +++ /dev/null @@ -1,117 +0,0 @@ -import java.sql.Connection -import java.sql.PreparedStatement -import scala.concurrent.Future -import scala.concurrent.{ExecutionContext => EC} -import scala.util.Failure -import scala.util.Success -import scala.util.Try - -object `package` { - type JDBC[A] = Connection => A - type Error = (Int, String) -} - -object Service { - - def unsafeRun[A](s: Service[A], c: Connection)(implicit - ec: EC - ): Future[Either[Error, A]] = - c synchronized { - val ea = - s.run(c) map { - case r @ Right(_) => - c.commit() - r - case l @ Left(_) => - c.rollback() - l - } - ea - } - - def apply[A](a: => A)(implicit ec: EC): Service[A] = - Service { _ => - Future { - Try(a) match { - case Failure(t) => Left((500, t.getMessage)) - case Success(x) => Right(x) - } - } - } - - def apply[A](k: Connection => A)(implicit ec: EC): Service[A] = - Service { c => - Future { - Try(k(c)) match { - case Failure(t) => Left((500, t.getMessage)) - case Success(x) => Right(x) - } - } - } - - def update(u: String)(implicit ec: EC): Service[Unit] = - apply { c => - val s = c.createStatement() - s.executeUpdate(u) - s.close() - } - - def update(q: String, k: PreparedStatement => Unit)(implicit - ec: EC - ): Service[Unit] = - apply { c => - val s = c.prepareStatement(q) - k(s) - s.executeUpdate() - s.close() - } - - def query[A](q: String, k: PreparedStatement => A)(implicit - ec: EC - ): Service[A] = - apply { c => - val s = c.prepareStatement(q) - val a = k(s) - s.close() - a - } -} - -case class Service[A](run: JDBC[Future[Either[Error, A]]]) { - - def map[B](f: A => B)(implicit ec: EC): Service[B] = - Service { c => - run(c) map { - case Left(e) => Left(e) - case Right(a) => Right(f(a)) - } - } - - def mapLeft(f: Error => Error)(implicit ec: EC): Service[A] = - Service { c => - run(c) map { - case Left(e) => Left(f(e)) - case Right(a) => Right(a) - } - } - - def withLeft(f: Error => Unit)(implicit ec: EC): Service[A] = - Service { c => - run(c) map { - case Left(e) => f(e); Left(e) - case Right(a) => Right(a) - } - } - - def flatMap[B](f: A => Service[B])(implicit ec: EC): Service[B] = - Service { c => - run(c) flatMap { - case Left(e) => Future(Left(e)) - case Right(a) => f(a).run(c) - } - } - - def >>[B](x: Service[B])(implicit ec: EC): Service[B] = - flatMap { _ => x } - -} diff --git a/examples/adder/src/main/scala/servlet.scala b/examples/adder/src/main/scala/servlet.scala deleted file mode 100644 index d83c301d..00000000 --- a/examples/adder/src/main/scala/servlet.scala +++ /dev/null @@ -1,115 +0,0 @@ -import java.sql.Connection -import java.sql.DriverManager -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.servlet.AsyncContext -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse -import scala.concurrent.Await -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.concurrent.duration.Duration -import scala.io.Source - -trait JdbcServlet { - - val c: Connection = { - Class.forName(sys.env("DB_DRIVER")) - val c: Connection = - DriverManager.getConnection( - sys.env("DB_URL"), - sys.env("DB_USER"), - sys.env("DB_PASS") - ) - c.setAutoCommit(false) - c - } - - def unsafeRun[A](ctx: AsyncContext)(s: Service[A]): Future[Unit] = - Service.unsafeRun(s, c) map { _ => ctx.complete() } -} - -trait CommandServlet extends HttpServlet with JdbcServlet { - - override def doPost( - req: HttpServletRequest, - res: HttpServletResponse - ) { - unsafeRun(req.startAsync()) { - Service { - Source.fromInputStream(req.getInputStream).mkString.toInt - } mapLeft { _ => - (400, "couldn't parse number") - } flatMap { amount => - Adder.add(amount) - } map { _ => - res.setStatus(201) - } withLeft { case (status, message) => - res.setContentType("text/plain") - res.setStatus(status) - res.getWriter.write(message) - } - } - } -} - -trait UpdateServlet extends HttpServlet with JdbcServlet { - - private val updater: Thread = - new Thread { - override def run(): Unit = { - while (!isInterrupted) { - Service.unsafeRun(Adder.update, c) - Thread.sleep(250) - } - } - } - - override def init(): Unit = { - updater.start - super.init() - } - - override def destroy(): Unit = { - updater.interrupt - super.destroy() - } -} - -trait QueryServlet extends HttpServlet with JdbcServlet { - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ) { - unsafeRun(req.startAsync()) { - Adder.getSum map { case (_, result) => - res.setContentType("text/plain") - res.setCharacterEncoding("UTF-8") - res.getWriter.write(s"${result}\n") - } withLeft { case (status, message) => - res.setContentType("text/plain") - res.setStatus(status) - res.getWriter.write(message) - } - } - } -} - -class AdderServlet - extends CommandServlet - with UpdateServlet - with QueryServlet { - - override def init(): Unit = { - Await.result( - Service.unsafeRun(Adder.init, c), - Duration(5000, MILLISECONDS) - ) - super.init() - } - - override def destroy(): Unit = { - super.destroy() - } -} diff --git a/examples/adder/src/main/webapp/WEB-INF/web.xml b/examples/adder/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 2bc4e4b3..00000000 --- a/examples/adder/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - AdderServlet - AdderServlet - true - - - - AdderServlet - /* - - - diff --git a/examples/adder/src/test/scala/Http.scala b/examples/adder/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/adder/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/adder/src/test/scala/TestSuite.scala b/examples/adder/src/test/scala/TestSuite.scala deleted file mode 100644 index 7e7042d4..00000000 --- a/examples/adder/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,70 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("get sum") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - status = 200, - Map("Content-Type" -> "text/plain;charset=utf-8"), - "0\n" - ) - } - - test("add some numbers") { - Request( - "POST", - "http://localhost:8080/", - Map.empty, - Some("1") - ) shouldBe - Response(status = 201, Map.empty, "") - - Request( - "POST", - "http://localhost:8080/", - Map.empty, - Some("11") - ) shouldBe - Response(status = 201, Map.empty, "") - - Request( - "POST", - "http://localhost:8080/", - Map.empty, - Some("13") - ) shouldBe - Response(status = 201, Map.empty, "") - - Request( - "POST", - "http://localhost:8080/", - Map.empty, - Some("17") - ) shouldBe - Response(status = 201, Map.empty, "") - - Thread.sleep(300) - - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - status = 200, - Map("Content-Type" -> "text/plain;charset=utf-8"), - "42\n" - ) - } - - test("post an unparseable number") { - Request( - "POST", - "http://localhost:8080/", - Map.empty, - Some("forty-two") - ) shouldBe - Response( - status = 400, - Map("Content-Type" -> "text/plain;charset=iso-8859-1"), - "couldn't parse number" - ) - } -} diff --git a/examples/async/README.md b/examples/async/README.md deleted file mode 100644 index 92833f34..00000000 --- a/examples/async/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - add a message -[info] Run completed in 783 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 3 s, completed Nov 13, 2022 1:51:05 PM -[info] waiting for server to shut down... -``` diff --git a/examples/async/build.sbt b/examples/async/build.sbt deleted file mode 100644 index 6b942f4a..00000000 --- a/examples/async/build.sbt +++ /dev/null @@ -1,4 +0,0 @@ -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" - -enablePlugins(JettyPlugin) diff --git a/examples/async/project/build.properties b/examples/async/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/async/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/async/project/plugins.sbt b/examples/async/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/async/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/async/src/main/scala/AsyncServlet.scala b/examples/async/src/main/scala/AsyncServlet.scala deleted file mode 100644 index bbc93bdc..00000000 --- a/examples/async/src/main/scala/AsyncServlet.scala +++ /dev/null @@ -1,40 +0,0 @@ -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -class AsyncServlet extends HttpServlet { - - val execSvc = java.util.concurrent.Executors.newFixedThreadPool(8) - - override def destroy(): Unit = { - execSvc.shutdown - } - - override def service( - req: HttpServletRequest, - res: HttpServletResponse - ) { - - val ctx = req.startAsync - - execSvc submit { - new Runnable() { - override def run(): Unit = { - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - val responseBody: String = - """ - | - |

Hello, world!

- | - |""".stripMargin - res.getWriter.write(responseBody) - - ctx.complete - } - } - } - } -} diff --git a/examples/async/src/main/webapp/WEB-INF/web.xml b/examples/async/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 3fd3142c..00000000 --- a/examples/async/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - AsyncServlet - AsyncServlet - true - - - - AsyncServlet - /* - - - diff --git a/examples/async/src/test/scala/Http.scala b/examples/async/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/async/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/async/src/test/scala/TestSuite.scala b/examples/async/src/test/scala/TestSuite.scala deleted file mode 100644 index 3a210fdd..00000000 --- a/examples/async/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,18 +0,0 @@ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class TestSuite extends AnyFunSuite with Matchers { - - test("add a message") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - """| - | - |

Hello, world!

- | - |""".stripMargin - ) - } -} diff --git a/examples/free/README.md b/examples/free/README.md deleted file mode 100644 index a4ea1e10..00000000 --- a/examples/free/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - require session -[info] - require valid session -[info] - sign in -[info] Run completed in 663 milliseconds. -[info] Total number of tests run: 3 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 3, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 4 s, completed Nov 13, 2022 1:52:46 PM -[info] waiting for server to shut down... -``` diff --git a/examples/free/build.sbt b/examples/free/build.sbt deleted file mode 100644 index f0f8fa26..00000000 --- a/examples/free/build.sbt +++ /dev/null @@ -1,25 +0,0 @@ -scalaVersion := "2.13.14" // needed for fancy type inference -scalacOptions ++= Seq( - "-deprecation", - "-encoding", - "utf8", - "-feature", - "-language:existentials", - "-language:experimental.macros", - "-language:higherKinds", - "-language:implicitConversions", - "-unchecked", - "-Xfatal-warnings", - "-Xlint", - "-Yrangepos", - "-Ywarn-unused" -) - -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(JettyPlugin) - -addCompilerPlugin( - "org.typelevel" %% "kind-projector" % "0.13.3" cross CrossVersion.full -) diff --git a/examples/free/project/build.properties b/examples/free/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/free/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/free/project/plugins.sbt b/examples/free/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/free/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/free/src/main/scala/Database.scala b/examples/free/src/main/scala/Database.scala deleted file mode 100644 index de010225..00000000 --- a/examples/free/src/main/scala/Database.scala +++ /dev/null @@ -1,31 +0,0 @@ -class Database { - - private val permissions: Map[String, Set[Permission]] = - Map( - "tbuckland" -> Set(Read, Write), - "lbracco" -> Set(Write), - "jdoe" -> Set(Read) - ) - - private val passwordsByUsername: Map[String, String] = - Map( - "tbuckland" -> "alligator3", - "lbracco" -> "secret", - "jdoe" -> "unguessable" - ) - - private var sessions: Map[String, User] = - Map.empty - - def getPermissions(username: String): Set[Permission] = - permissions.getOrElse(username, Set.empty) - - def findUsernameAndPassword( - username: String, - password: String - ): Boolean = - passwordsByUsername.get(username) == Some(password) - - def addSession(sessionId: String, user: User): Unit = - sessions = sessions + (sessionId -> user) -} diff --git a/examples/free/src/main/scala/FreeMonad.scala b/examples/free/src/main/scala/FreeMonad.scala deleted file mode 100644 index 089c56b8..00000000 --- a/examples/free/src/main/scala/FreeMonad.scala +++ /dev/null @@ -1,40 +0,0 @@ -trait ~>[F[_], G[_]] { - def apply[A](f: F[A]): G[A] -} - -trait Monad[F[_]] { - def pure[A](a: A): F[A] - def bind[A, B](fa: F[A])(f: A => F[B]): F[B] -} - -sealed trait Free[F[_], A] { - - import Free._ - - def map[B](f: A => B): Free[F, B] = flatMap { a => pure(f(a)) } - - def flatMap[B](f: A => Free[F, B]): Free[F, B] = Bind(this, f) - - def foldMap[G[_]: Monad](nt: F ~> G): G[A] = - this match { - case Pure(a) => implicitly[Monad[G]].pure(a) - case Suspend(fa) => nt(fa) - case Bind(fa, f) => - val mg = implicitly[Monad[G]] - val ga = fa.foldMap(nt) - mg.bind(ga)(f(_).foldMap(nt)) - } - -} - -object Free { - - def pure[F[_], A](a: A): Free[F, A] = Pure(a) - - def liftM[F[_], A](fa: F[A]): Free[F, A] = Suspend(fa) - - final case class Pure[F[_], A](a: A) extends Free[F, A] - final case class Suspend[F[_], A](fa: F[A]) extends Free[F, A] - final case class Bind[F[_], A, B](fa: Free[F, A], f: A => Free[F, B]) - extends Free[F, B] -} diff --git a/examples/free/src/main/scala/FreeServlet.scala b/examples/free/src/main/scala/FreeServlet.scala deleted file mode 100644 index 2c11317e..00000000 --- a/examples/free/src/main/scala/FreeServlet.scala +++ /dev/null @@ -1,73 +0,0 @@ -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -class FreeServlet extends HttpServlet { - - implicit val responseMonad: Monad[Either[WebAppErr, *]] = - new Monad[Either[WebAppErr, *]] { - def pure[A](a: A): Either[WebAppErr, *][A] = Right(a) - def bind[A, B]( - fa: Either[WebAppErr, *][A] - )(f: A => Either[WebAppErr, *][B]): Either[WebAppErr, *][B] = - fa match { - case Left(e) => Left(e) - case Right(a) => f(a) - } - } - - val database = new Database - - def interpreter( - req: HttpServletRequest - ): WebAppOp ~> Either[WebAppErr, *] = - new ~>[WebAppOp, Either[WebAppErr, *]] { - def apply[A](c: WebAppOp[A]): Either[WebAppErr, *][A] = - try { - c match { - case Value(x) => Right(x) - case GetDatabase => Right(database) - case RequestHeader(name) => - Option(req.getHeader(name)) - .map(Right(_)) - .getOrElse(Left(MissingHeader(name))) - case RequestParam(name) => - Option(req.getParameter(name)) - .map(Right(_)) - .getOrElse(Left(MissingParameter(name))) - case Error(e) => Left(e) - } - } catch { - case t: Throwable => - Left(InternalServerError(t)) - } - } - - override def doPost( - req: HttpServletRequest, - res: HttpServletResponse - ): Unit = { - WebAppOp.signIn.foldMap(interpreter(req)) match { - case Right(sessionId) => - res.setStatus(201) - res.setHeader("X-Session-ID", sessionId) - case Left(e) => - res.setStatus(e.status) - res.getWriter.write(e.message) - } - } - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ): Unit = { - WebAppOp.getSecret.foldMap(interpreter(req)) match { - case Right(secret) => - res.setStatus(200) - res.getWriter.write(secret) - case Left(e) => - res.setStatus(e.status) - res.getWriter.write(e.message) - } - } -} diff --git a/examples/free/src/main/scala/WebAppOp.scala b/examples/free/src/main/scala/WebAppOp.scala deleted file mode 100644 index 09a740b7..00000000 --- a/examples/free/src/main/scala/WebAppOp.scala +++ /dev/null @@ -1,114 +0,0 @@ -import java.util.UUID - -case class User(username: String) - -sealed trait Permission -case object Read extends Permission -case object Write extends Permission -case object Delete extends Permission - -sealed trait WebAppOp[+A] -case class Value[A](value: A) extends WebAppOp[A] -case object GetDatabase extends WebAppOp[Database] -case class RequestParam(name: String) extends WebAppOp[String] -case class RequestHeader(name: String) extends WebAppOp[String] -case class Error(e: WebAppErr) extends WebAppOp[Nothing] - -sealed trait WebAppErr { - def status: Int - def message: String -} -case class InsufficientPermissions( - required: Set[Permission], - permissions: Set[Permission] -) extends WebAppErr { - val status: Int = 403 - val message: String = - s"Insufficient permissions; required: ${required}, present: ${permissions}" -} -case class InvalidSession(sessionId: String) extends WebAppErr { - val status: Int = 401 - val message: String = s"Invalid session: ${sessionId}" -} -case class InvalidUsernameOrPassword(username: String) - extends WebAppErr { - val status: Int = 404 - val message: String = - s"Invalid username or password; username: ${username}" -} -case class MissingParameter(name: String) extends WebAppErr { - val status: Int = 400 - val message: String = s"Missing required parameter: ${name}" -} -case class MissingHeader(name: String) extends WebAppErr { - val status: Int = 400 - val message: String = s"Missing required header: ${name}" -} -case class InternalServerError(throwable: Throwable) extends WebAppErr { - val status: Int = 500 - val message: String = throwable.getMessage -} - -object WebAppOp { - - private val permissions: Map[String, Set[Permission]] = - Map( - "tbuckland" -> Set(Read, Write), - "lbracco" -> Set(Write), - "jdoe" -> Set(Read) - ) - - private val passwordsByUsername: Map[String, String] = - Map( - "tbuckland" -> "alligator3", - "lbracco" -> "secret", - "jdoe" -> "unguessable" - ) - - private var sessions: Map[String, User] = - Map.empty - - def signIn: Free[WebAppOp, String] = - for { - username <- Free.liftM(RequestParam("username")) - password <- Free.liftM(RequestParam("password")) - sessionId <- Free.liftM { - if (passwordsByUsername.get(username) == Some(password)) { - val sessionId = UUID.randomUUID.toString - val user = User(username) - sessions = sessions + (sessionId -> user) - Value(sessionId) - } else { - Error(InvalidUsernameOrPassword(username)) - } - } - } yield sessionId - - def authN: Free[WebAppOp, User] = - for { - sessionId <- Free.liftM(RequestHeader("x-session-id")) - user <- Free.liftM { - sessions.get(sessionId) match { - case Some(user) => Value(user) - case None => Error(InvalidSession(sessionId)) - } - } - } yield user - - def authZ[A](required: Set[Permission], k: => A): Free[WebAppOp, A] = - for { - perms <- getPermissions - result <- Free.liftM { - if ((required -- perms).isEmpty) Value(k) - else Error(InsufficientPermissions(required, perms)) - } - } yield result - - val getPermissions: Free[WebAppOp, Set[Permission]] = - for { - user <- WebAppOp.authN - } yield permissions.getOrElse(user.username, Set.empty) - - val getSecret: Free[WebAppOp, String] = - authZ(Set(Read), "The duck flies at midnight.") -} diff --git a/examples/free/src/main/webapp/WEB-INF/web.xml b/examples/free/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 28d52e2f..00000000 --- a/examples/free/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - free - FreeServlet - - - - free - /* - - - diff --git a/examples/free/src/test/scala/Http.scala b/examples/free/src/test/scala/Http.scala deleted file mode 100644 index 591bbfef..00000000 --- a/examples/free/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.jdk.CollectionConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/free/src/test/scala/TestSuite.scala b/examples/free/src/test/scala/TestSuite.scala deleted file mode 100644 index 2cba81c3..00000000 --- a/examples/free/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,39 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("require session") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response(400, Map.empty, "Missing required header: x-session-id") - } - - test("require valid session") { - Request( - "GET", - "http://localhost:8080/", - Map("X-Session-ID" -> "foo123"), - None - ) shouldBe - Response(401, Map.empty, "Invalid session: foo123") - } - - test("sign in") { - val sessionId = - Request( - "POST", - "http://localhost:8080/", - Map.empty, - Some("username=tbuckland&password=alligator3") - ).headers - .get("X-Session-ID") - .get - - Request( - "GET", - "http://localhost:8080/", - Map("X-Session-ID" -> sessionId), - None - ) shouldBe - Response(200, Map.empty, "The duck flies at midnight.") - } -} diff --git a/examples/frege/README.md b/examples/frege/README.md deleted file mode 100644 index bb298dae..00000000 --- a/examples/frege/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] - /hello -[info] - /foo -[info] Run completed in 407 milliseconds. -[info] Total number of tests run: 3 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 3, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 4 s, completed Nov 13, 2022 1:54:19 PM -[info] waiting for server to shut down... -``` diff --git a/examples/frege/build.sbt b/examples/frege/build.sbt deleted file mode 100644 index 07d1cef6..00000000 --- a/examples/frege/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -enablePlugins(JettyPlugin) - -javacOptions ++= Seq("-encoding", "UTF-8") - -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" diff --git a/examples/frege/project/build.properties b/examples/frege/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/frege/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/frege/project/plugins.sbt b/examples/frege/project/plugins.sbt deleted file mode 100644 index 817df926..00000000 --- a/examples/frege/project/plugins.sbt +++ /dev/null @@ -1,3 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") - -addSbtPlugin("com.earldouglas" % "sbt-frege" % "3.0.2") diff --git a/examples/frege/src/main/frege/FregeWeb.fr b/examples/frege/src/main/frege/FregeWeb.fr deleted file mode 100644 index ca63677a..00000000 --- a/examples/frege/src/main/frege/FregeWeb.fr +++ /dev/null @@ -1,12 +0,0 @@ -module fregeweb.FregeWeb where - -data Request = Request { method :: String, uri :: String } -data Response = Response { status :: Int, body :: String } - -service :: Request -> Response -service (Request "GET" "/") = response 200 "hello" -service (Request "GET" "/hello") = response 200 "Hello, world!" -service _ = response 404 "404'd!" - -response :: Int -> String -> Response -response status body = Response status body diff --git a/examples/frege/src/main/scala/FregeServlet.scala b/examples/frege/src/main/scala/FregeServlet.scala deleted file mode 100644 index 425edce1..00000000 --- a/examples/frege/src/main/scala/FregeServlet.scala +++ /dev/null @@ -1,51 +0,0 @@ -package fregeweb - -import scala.language.implicitConversions - -import javax.servlet.http.HttpServlet -import javax.servlet.http.{HttpServletRequest => HSReq} -import javax.servlet.http.{HttpServletResponse => HSRes} - -import FregeWeb.TRequest -import FregeWeb.TResponse - -import frege.run8.Box - -class FregeServlet extends HttpServlet { - - override def service(hsReq: HSReq, hsRes: HSRes): Unit = - hsRes service hsReq - -} - -object `package` { - - implicit class HSResService(val hsRes: HSRes) extends AnyVal { - - def service(hsReq: HSReq): Unit = { - val tReq: TRequest = - TRequest.mk(new Box(hsReq.method), new Box(hsReq.uri)) - val tRes: TResponse = - FregeWeb.service(tReq).asInstanceOf[TResponse] - write(tRes) - } - - private def write(tRes: TResponse): Unit = { - val status: Int = TResponse.status(tRes).asInstanceOf[Int] - val body: String = TResponse.body(tRes).asInstanceOf[String] - hsRes.setStatus(status) - hsRes.getWriter().write(body) - } - - } - - implicit class RichHSReq(val hsReq: HSReq) extends AnyVal { - def method: String = hsReq.getMethod() - def uri: String = - if (hsReq.getRequestURI().startsWith(hsReq.getServletPath())) - hsReq.getRequestURI().substring(hsReq.getServletPath().length()) - else - hsReq.getRequestURI() - } - -} diff --git a/examples/frege/src/main/webapp/WEB-INF/web.xml b/examples/frege/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 2af0bc3e..00000000 --- a/examples/frege/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - frege-web - fregeweb.FregeServlet - - - - frege-web - /* - - - diff --git a/examples/frege/src/test/scala/Http.scala b/examples/frege/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/frege/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/frege/src/test/scala/TestSuite.scala b/examples/frege/src/test/scala/TestSuite.scala deleted file mode 100644 index 10ef6b25..00000000 --- a/examples/frege/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,34 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("/") { - Request( - "GET", - "http://localhost:8080/", - Map.empty, - body = None - ) shouldBe - Response(200, Map.empty, """hello""") - } - - test("/hello") { - Request( - "GET", - "http://localhost:8080/hello", - Map.empty, - None - ) shouldBe - Response(200, Map.empty, "Hello, world!") - } - - test("/foo") { - Request( - "GET", - "http://localhost:8080/foo", - Map.empty, - None - ) shouldBe - Response(404, Map.empty, "404'd!") - } -} diff --git a/examples/getting-started/README.md b/examples/getting-started/README.md deleted file mode 100644 index f599860e..00000000 --- a/examples/getting-started/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] Run completed in 739 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 3 s, completed Nov 13, 2022 1:55:49 PM -[info] waiting for server to shut down... -``` diff --git a/examples/getting-started/build.sbt b/examples/getting-started/build.sbt deleted file mode 100644 index 23e477bd..00000000 --- a/examples/getting-started/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" -enablePlugins(JettyPlugin) diff --git a/examples/getting-started/project/build.properties b/examples/getting-started/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/getting-started/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/getting-started/project/plugins.sbt b/examples/getting-started/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/getting-started/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/getting-started/src/main/scala/GettingStartedServlet.scala b/examples/getting-started/src/main/scala/GettingStartedServlet.scala deleted file mode 100644 index d0e2ec02..00000000 --- a/examples/getting-started/src/main/scala/GettingStartedServlet.scala +++ /dev/null @@ -1,23 +0,0 @@ -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -class GettingStartedServlet extends HttpServlet { - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ) { - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - val responseBody: String = - """| - | - |

Hello, world!

- | - |""".stripMargin - res.getWriter.write(responseBody) - } -} diff --git a/examples/getting-started/src/main/webapp/WEB-INF/web.xml b/examples/getting-started/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d86c711d..00000000 --- a/examples/getting-started/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - getting started - GettingStartedServlet - - - - getting started - /* - - - diff --git a/examples/getting-started/src/test/scala/Http.scala b/examples/getting-started/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/getting-started/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/getting-started/src/test/scala/TestSuite.scala b/examples/getting-started/src/test/scala/TestSuite.scala deleted file mode 100644 index 12a38709..00000000 --- a/examples/getting-started/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,18 +0,0 @@ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class TestSuite extends AnyFunSuite with Matchers { - - test("/") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - body = """| - | - |

Hello, world!

- | - |""".stripMargin - ) - } -} diff --git a/examples/http4s/README.md b/examples/http4s/README.md deleted file mode 100644 index 3ccae223..00000000 --- a/examples/http4s/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - hello -[info] Run completed in 1 second, 858 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 4 s, completed Nov 13, 2022 1:59:36 PM -[info] waiting for server to shut down... -``` diff --git a/examples/http4s/build.sbt b/examples/http4s/build.sbt deleted file mode 100644 index 8e3ae9a8..00000000 --- a/examples/http4s/build.sbt +++ /dev/null @@ -1,14 +0,0 @@ -val http4sVersion = "1.0.0-M38" - -ThisBuild / scalaVersion := "3.2.2" - -libraryDependencies += "org.http4s" %% "http4s-dsl" % http4sVersion -libraryDependencies += "org.http4s" %% "http4s-servlet" % http4sVersion -libraryDependencies += "jakarta.servlet" % "jakarta.servlet-api" % "6.0.0" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" - -Jetty / containerLibs := Seq( - "org.eclipse.jetty" % "jetty-runner" % "11.0.15" intransitive () -) - -enablePlugins(JettyPlugin) diff --git a/examples/http4s/project/build.properties b/examples/http4s/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/http4s/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/http4s/project/plugins.sbt b/examples/http4s/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/http4s/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/http4s/shell.nix b/examples/http4s/shell.nix deleted file mode 100644 index 3d4d62d0..00000000 --- a/examples/http4s/shell.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs ? import {} }: -let - jdk = pkgs.jdk11; -in - pkgs.mkShell { - nativeBuildInputs = [ - (pkgs.sbt.override { jre = jdk; }) - ]; - shellHook = '' - export JAVA_HOME=${jdk} - PATH="${jdk}/bin:$PATH" - ''; - } diff --git a/examples/http4s/src/main/scala/HelloWorldServlet.scala b/examples/http4s/src/main/scala/HelloWorldServlet.scala deleted file mode 100644 index 02d3e28b..00000000 --- a/examples/http4s/src/main/scala/HelloWorldServlet.scala +++ /dev/null @@ -1,25 +0,0 @@ -import cats.effect.IO -import cats.effect.std.Dispatcher -import cats.effect.unsafe.IORuntime -import org.http4s.implicits._ -import org.http4s.server.DefaultServiceErrorHandler -import org.http4s.servlet.AsyncHttp4sServlet -import org.http4s.servlet.NonBlockingServletIo -import scala.concurrent.ExecutionContext -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.concurrent.duration.Duration - -class HelloWorldServlet - extends AsyncHttp4sServlet[IO]( - httpApp = Services.helloWorldService.orNotFound, - asyncTimeout = Duration.Inf, - servletIo = NonBlockingServletIo(4096), - serviceErrorHandler = DefaultServiceErrorHandler, - dispatcher = new Dispatcher[IO] { - override def unsafeToFutureCancelable[A]( - fa: IO[A] - ): (Future[A], () => Future[Unit]) = - (fa.unsafeToFuture()(IORuntime.global), () => Future(())) - } - )(IO.asyncForIO) diff --git a/examples/http4s/src/main/scala/Services.scala b/examples/http4s/src/main/scala/Services.scala deleted file mode 100644 index bf80867d..00000000 --- a/examples/http4s/src/main/scala/Services.scala +++ /dev/null @@ -1,11 +0,0 @@ -import cats.effect.IO -import org.http4s.HttpRoutes -import org.http4s.dsl.io._ - -object Services { - - val helloWorldService = - HttpRoutes.of[IO] { case GET -> Root / "hello" / name => - Ok(s"Hello, $name.") - } -} diff --git a/examples/http4s/src/main/webapp/WEB-INF/web.xml b/examples/http4s/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 656a7d1b..00000000 --- a/examples/http4s/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - hello world - HelloWorldServlet - true - - - - hello world - /* - - - diff --git a/examples/http4s/src/test/scala/Http.scala b/examples/http4s/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/http4s/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/http4s/src/test/scala/TestSuite.scala b/examples/http4s/src/test/scala/TestSuite.scala deleted file mode 100644 index f17e73fe..00000000 --- a/examples/http4s/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,19 +0,0 @@ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class TestSuite extends AnyFunSuite with Matchers { - - test("hello") { - Request( - "GET", - "http://localhost:8080/hello/James", - Map.empty, - None - ) shouldBe - Response( - status = 200, - Map("Content-Type" -> "text/plain;charset=utf-8"), - "Hello, James." - ) - } -} diff --git a/examples/https/README.md b/examples/https/README.md deleted file mode 100644 index 73a09283..00000000 --- a/examples/https/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# HTTPS with Tomcat - -Let's serve our project over HTTPS directly from sbt. - -## Creating a certificate - -Create a basic self-signed certificate, KeyStore, and TrustStore by -following the steps [in this tutorial][server-cert-tutorial]. By -convention, passwords throughout will be `changeit`. - -[server-cert-tutorial]: https://docs.oracle.com/cd/E19798-01/821-1841/gjrgy/index.html) - -``` -$ keytool -genkey -alias server-alias -keyalg RSA -keypass changeit \ - -storepass changeit -keystore keystore.jks -What is your first and last name? - [Unknown]: -What is the name of your organizational unit? - [Unknown]: -What is the name of your organization? - [Unknown]: -What is the name of your City or Locality? - [Unknown]: -What is the name of your State or Province? - [Unknown]: -What is the two-letter country code for this unit? - [Unknown]: -Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? - [no]: yes -``` - -``` -$ keytool -export -alias server-alias -storepass changeit \ - -file server.cer -keystore keystore.jks -Certificate stored in file -``` - -``` -$ keytool -import -v -trustcacerts -alias server-alias \ - -file server.cer -keystore cacerts.jks -keypass changeit \ - -storepass changeit -Trust this certificate? [no]: yes -Certificate was added to keystore -[Storing cacerts.jks] -``` - -## Configuring xsbt-web-plugin - -We'll use Tomcat: - -```scala -enablePlugins(TomcatPlugin) -``` - -The Tomcat plugin uses [webapp-runner], which allows us to enable SSL -via `--enable-ssl`: - -[webapp-runner]: https://github.com/jsimone/webapp-runner - -```scala -containerArgs := Seq( - "--enable-ssl" -) -``` - -To enable SSL, we need to point the JVM toward our KeyStore, TrustStore, -and proivde the corresponding passwords: - -```scala -javaOptions in Tomcat ++= Seq( - "-Djavax.net.ssl.keyStore=keystore.jks", - "-Djavax.net.ssl.keyStorePassword=changeit", - "-Djavax.net.ssl.trustStore=cacerts.jks", - "-Djavax.net.ssl.trustStorePassword=changeit" -) -``` - -By default, xsbt-web-plugin binds our project to port 8080, but let's -use something more HTTPS-ish instead: - -```scala -containerPort := 8443 -``` - -Now we can run our project from sbt with `tomcat:start`, and view it at -`https://localhost:8443/`. Since we used a self-signed certificate, -we may need to reassure our Web browser that it's safe to proceed. - -``` -$ curl -ik https://localhost:8443 -HTTP/1.1 200 -Content-Type: text/html;charset=UTF-8 -Content-Length: 60 -Date: Wed, 06 Jun 2018 18:48:42 GMT - - - -

Hello, world!

- - -``` - -## Testing - -``` -$ sbt Tomcat/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] Run completed in 1 second, 806 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 5 s, completed Nov 13, 2022 2:03:45 PM -[info] waiting for server to shut down... -``` diff --git a/examples/https/build.sbt b/examples/https/build.sbt deleted file mode 100644 index 0c55c928..00000000 --- a/examples/https/build.sbt +++ /dev/null @@ -1,17 +0,0 @@ -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(TomcatPlugin) - -containerArgs := Seq( - "--enable-ssl" -) - -Tomcat / javaOptions ++= Seq( - "-Djavax.net.ssl.keyStore=keystore.jks", - "-Djavax.net.ssl.keyStorePassword=changeit", - "-Djavax.net.ssl.trustStore=cacerts.jks", - "-Djavax.net.ssl.trustStorePassword=changeit" -) - -containerPort := 8443 diff --git a/examples/https/cacerts.jks b/examples/https/cacerts.jks deleted file mode 100644 index 0eb1befd..00000000 Binary files a/examples/https/cacerts.jks and /dev/null differ diff --git a/examples/https/keystore.jks b/examples/https/keystore.jks deleted file mode 100644 index 2ecf7c10..00000000 Binary files a/examples/https/keystore.jks and /dev/null differ diff --git a/examples/https/project/build.properties b/examples/https/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/https/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/https/project/plugins.sbt b/examples/https/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/https/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/https/server.cer b/examples/https/server.cer deleted file mode 100644 index fe544151..00000000 Binary files a/examples/https/server.cer and /dev/null differ diff --git a/examples/https/src/main/scala/GettingStartedServlet.scala b/examples/https/src/main/scala/GettingStartedServlet.scala deleted file mode 100644 index 97e78a59..00000000 --- a/examples/https/src/main/scala/GettingStartedServlet.scala +++ /dev/null @@ -1,23 +0,0 @@ -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -class GettingStartedServlet extends HttpServlet { - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ) { - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - val responseBody: String = - """ - | - |

Hello, world!

- | - |""".stripMargin - res.getWriter.write(responseBody) - } -} diff --git a/examples/https/src/main/webapp/WEB-INF/web.xml b/examples/https/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d86c711d..00000000 --- a/examples/https/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - getting started - GettingStartedServlet - - - - getting started - /* - - - diff --git a/examples/https/src/test/scala/Http.scala b/examples/https/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/https/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/https/src/test/scala/TestSuite.scala b/examples/https/src/test/scala/TestSuite.scala deleted file mode 100644 index e1b511cf..00000000 --- a/examples/https/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,54 +0,0 @@ -import java.security.cert._ -import javax.net.ssl._ -import org.scalatest._ -import java.security.SecureRandom - -class TestSuite extends FunSuite with BeforeAndAfterAll with Matchers { - - override def beforeAll(): Unit = { - val sslContext = SSLContext.getInstance("SSL") - sslContext.init( - null, - Array( - new X509TrustManager { - val getAcceptedIssuers = null - def checkClientTrusted( - x509Certificates: Array[X509Certificate], - s: String - ) = {} - def checkServerTrusted( - x509Certificates: Array[X509Certificate], - s: String - ) = {} - } - ), - new SecureRandom() - ) - - HttpsURLConnection.setDefaultSSLSocketFactory( - sslContext.getSocketFactory - ) - HttpsURLConnection.setDefaultHostnameVerifier( - new HostnameVerifier { - def verify(s: String, sslSession: SSLSession) = true - } - ) - } - - test("/") { - Request("GET", "https://localhost:8443/", Map.empty, None) shouldBe - Response( - 200, - Map( - "Keep-Alive" -> "timeout=60", - "Connection" -> "keep-alive", - "Content-Type" -> "text/html;charset=UTF-8" - ), - """ - | - |

Hello, world!

- | - |""".stripMargin - ) - } -} diff --git a/examples/jetty-11/README.md b/examples/jetty-11/README.md deleted file mode 100644 index f599860e..00000000 --- a/examples/jetty-11/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] Run completed in 739 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 3 s, completed Nov 13, 2022 1:55:49 PM -[info] waiting for server to shut down... -``` diff --git a/examples/jetty-11/build.sbt b/examples/jetty-11/build.sbt deleted file mode 100644 index 7ff54747..00000000 --- a/examples/jetty-11/build.sbt +++ /dev/null @@ -1,8 +0,0 @@ -libraryDependencies += "jakarta.servlet" % "jakarta.servlet-api" % "5.0.0" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" - -enablePlugins(JettyPlugin) - -containerLibs in Jetty := Seq( - "org.eclipse.jetty" % "jetty-runner" % "11.0.15" intransitive () -) diff --git a/examples/jetty-11/project/build.properties b/examples/jetty-11/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/jetty-11/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/jetty-11/project/plugins.sbt b/examples/jetty-11/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/jetty-11/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/jetty-11/shell.nix b/examples/jetty-11/shell.nix deleted file mode 100644 index 3d4d62d0..00000000 --- a/examples/jetty-11/shell.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs ? import {} }: -let - jdk = pkgs.jdk11; -in - pkgs.mkShell { - nativeBuildInputs = [ - (pkgs.sbt.override { jre = jdk; }) - ]; - shellHook = '' - export JAVA_HOME=${jdk} - PATH="${jdk}/bin:$PATH" - ''; - } diff --git a/examples/jetty-11/src/main/scala/GettingStartedServlet.scala b/examples/jetty-11/src/main/scala/GettingStartedServlet.scala deleted file mode 100644 index 30d5a66e..00000000 --- a/examples/jetty-11/src/main/scala/GettingStartedServlet.scala +++ /dev/null @@ -1,23 +0,0 @@ -import jakarta.servlet.http.HttpServlet -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse - -class GettingStartedServlet extends HttpServlet { - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ) { - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - val responseBody: String = - """| - | - |

Hello, world!

- | - |""".stripMargin - res.getWriter.write(responseBody) - } -} diff --git a/examples/jetty-11/src/main/webapp/WEB-INF/web.xml b/examples/jetty-11/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d86c711d..00000000 --- a/examples/jetty-11/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - getting started - GettingStartedServlet - - - - getting started - /* - - - diff --git a/examples/jetty-11/src/test/scala/Http.scala b/examples/jetty-11/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/jetty-11/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/jetty-11/src/test/scala/TestSuite.scala b/examples/jetty-11/src/test/scala/TestSuite.scala deleted file mode 100644 index 12a38709..00000000 --- a/examples/jetty-11/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,18 +0,0 @@ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class TestSuite extends AnyFunSuite with Matchers { - - test("/") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - body = """| - | - |

Hello, world!

- | - |""".stripMargin - ) - } -} diff --git a/examples/lift/README.md b/examples/lift/README.md deleted file mode 100644 index d37ecf57..00000000 --- a/examples/lift/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] Run completed in 1 second, 81 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 5 s, completed Nov 13, 2022 2:05:08 PM -[info] waiting for server to shut down... -``` diff --git a/examples/lift/build.sbt b/examples/lift/build.sbt deleted file mode 100644 index cfe7b9e5..00000000 --- a/examples/lift/build.sbt +++ /dev/null @@ -1,5 +0,0 @@ -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "net.liftweb" %% "lift-webkit" % "3.5.0" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(JettyPlugin) diff --git a/examples/lift/project/build.properties b/examples/lift/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/lift/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/lift/project/plugins.sbt b/examples/lift/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/lift/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/lift/src/main/scala/Boot.scala b/examples/lift/src/main/scala/Boot.scala deleted file mode 100644 index 6741684f..00000000 --- a/examples/lift/src/main/scala/Boot.scala +++ /dev/null @@ -1,25 +0,0 @@ -package bootstrap.liftweb - -import net.liftweb.http.Html5Properties -import net.liftweb.http.LiftRules -import net.liftweb.http.Req -import net.liftweb.sitemap.Menu -import net.liftweb.sitemap.SiteMap - -class Boot { - - def boot { - - LiftRules.addToPackages("code") - - def sitemap(): SiteMap = SiteMap( - Menu.i("Home") / "index" - ) - - LiftRules.htmlProperties.default.set((r: Req) => - new Html5Properties(r.userAgent) - ) - - } - -} diff --git a/examples/lift/src/main/scala/code/snippet/HelloWorld.scala b/examples/lift/src/main/scala/code/snippet/HelloWorld.scala deleted file mode 100644 index 6b17e5a2..00000000 --- a/examples/lift/src/main/scala/code/snippet/HelloWorld.scala +++ /dev/null @@ -1,10 +0,0 @@ -package code.snippet - -import java.util.Date -import net.liftweb.util.Helpers._ - -class HelloWorld { - - def render = "* *" #> "Hello, world!" - -} diff --git a/examples/lift/src/main/webapp/WEB-INF/web.xml b/examples/lift/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 9d587e9b..00000000 --- a/examples/lift/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - LiftFilter - net.liftweb.http.LiftFilter - - - - LiftFilter - /* - - - diff --git a/examples/lift/src/main/webapp/index.html b/examples/lift/src/main/webapp/index.html deleted file mode 100644 index 6bd9ca35..00000000 --- a/examples/lift/src/main/webapp/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - Lift - - -

- - diff --git a/examples/lift/src/test/scala/Http.scala b/examples/lift/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/lift/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/lift/src/test/scala/TestSuite.scala b/examples/lift/src/test/scala/TestSuite.scala deleted file mode 100644 index a5fbaf30..00000000 --- a/examples/lift/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,14 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("/") { - val response = - Request("GET", "http://localhost:8080/", Map.empty, None) - response.status shouldBe 200 - response.headers.get("Content-Type") shouldBe Some( - "text/html;charset=utf-8" - ) - response.body.contains("Hello, world!") shouldBe true - } -} diff --git a/examples/mustache/README.md b/examples/mustache/README.md deleted file mode 100644 index a41a8ff8..00000000 --- a/examples/mustache/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] Run completed in 833 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 4 s, completed Nov 13, 2022 2:07:19 PM -[info] waiting for server to shut down... -``` diff --git a/examples/mustache/build.sbt b/examples/mustache/build.sbt deleted file mode 100644 index bf26d026..00000000 --- a/examples/mustache/build.sbt +++ /dev/null @@ -1,8 +0,0 @@ -libraryDependencies += "com.github.spullara.mustache.java" % "scala-extensions-2.12" % "0.9.14" -libraryDependencies += "com.github.spullara.mustache.java" % "compiler" % "0.9.14" -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(JettyPlugin) - -webappWebInfClasses := true diff --git a/examples/mustache/project/build.properties b/examples/mustache/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/mustache/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/mustache/project/plugins.sbt b/examples/mustache/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/mustache/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/mustache/src/main/resources/greeting.mustache b/examples/mustache/src/main/resources/greeting.mustache deleted file mode 100644 index c4507110..00000000 --- a/examples/mustache/src/main/resources/greeting.mustache +++ /dev/null @@ -1,5 +0,0 @@ - - -

{{ greeting }}

- - diff --git a/examples/mustache/src/main/scala/GettingStartedServlet.scala b/examples/mustache/src/main/scala/GettingStartedServlet.scala deleted file mode 100644 index 4ec08494..00000000 --- a/examples/mustache/src/main/scala/GettingStartedServlet.scala +++ /dev/null @@ -1,46 +0,0 @@ -import com.github.mustachejava.DefaultMustacheFactory -import com.twitter.mustache.ScalaObjectHandler -import java.io.File -import java.io.PrintWriter -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -object `package` { - - val mf = new DefaultMustacheFactory() - mf.setObjectHandler(new ScalaObjectHandler) - - implicit class MustacheResonse(res: HttpServletResponse) { - - def render(templateName: String, model: Any): Unit = { - - val path: String = - getClass.getClassLoader - .getResource("greeting.mustache") - .getFile - - val mustache = mf.compile(path) - - mustache.execute(res.getWriter, model) - } - - } - -} - -class MustacheServlet extends HttpServlet { - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ) { - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - res.render("greeting.mustache", Map("greeting" -> "Hello, world")) - - } - -} diff --git a/examples/mustache/src/main/webapp/WEB-INF/web.xml b/examples/mustache/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 6905e8b3..00000000 --- a/examples/mustache/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - mustache - MustacheServlet - - - - mustache - /* - - - diff --git a/examples/mustache/src/test/scala/Http.scala b/examples/mustache/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/mustache/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/mustache/src/test/scala/TestSuite.scala b/examples/mustache/src/test/scala/TestSuite.scala deleted file mode 100644 index faa1c654..00000000 --- a/examples/mustache/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,18 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("/") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - status = 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - """| - | - |

Hello, world

- | - | - |""".stripMargin - ) - } -} diff --git a/examples/payara-micro/README.md b/examples/payara-micro/README.md deleted file mode 100644 index 772afc51..00000000 --- a/examples/payara-micro/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Container/test -[info] starting server ... -[info] TestSuite: -[info] - hello -[info] Run completed in 8 seconds, 359 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 14 s, completed Nov 13, 2022 2:11:15 PM -[info] waiting for server to shut down... -``` diff --git a/examples/payara-micro/build.sbt b/examples/payara-micro/build.sbt deleted file mode 100644 index f94a44bb..00000000 --- a/examples/payara-micro/build.sbt +++ /dev/null @@ -1,23 +0,0 @@ -enablePlugins(ContainerPlugin) - -libraryDependencies += "jakarta.platform" % "jakarta.jakartaee-api" % "10.0.0" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -Container / javaOptions ++= - Seq( - "--add-opens", - "java.base/jdk.internal.loader=ALL-UNNAMED" - ) - -Container / containerLibs := - Seq("fish.payara.extras" % "payara-micro" % "6.2024.9") - -Container / containerLaunchCmd := { (port, path) => - Seq( - "fish.payara.micro.PayaraMicro", - "--deploy", - path, - "--contextroot", - "/mycontext" - ) -} diff --git a/examples/payara-micro/project/build.properties b/examples/payara-micro/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/payara-micro/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/payara-micro/project/plugins.sbt b/examples/payara-micro/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/payara-micro/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/payara-micro/src/main/scala/local/test/Main.scala b/examples/payara-micro/src/main/scala/local/test/Main.scala deleted file mode 100644 index 7fc1c31b..00000000 --- a/examples/payara-micro/src/main/scala/local/test/Main.scala +++ /dev/null @@ -1,7 +0,0 @@ -package local.test - -import jakarta.ws.rs.ApplicationPath -import jakarta.ws.rs.core.Application - -@ApplicationPath("myapp") -class Main extends Application {} diff --git a/examples/payara-micro/src/main/scala/local/test/endpoint/Hello.scala b/examples/payara-micro/src/main/scala/local/test/endpoint/Hello.scala deleted file mode 100644 index 625ac0a5..00000000 --- a/examples/payara-micro/src/main/scala/local/test/endpoint/Hello.scala +++ /dev/null @@ -1,19 +0,0 @@ -package local.test.endpoint - -import jakarta.ws.rs.GET -import jakarta.ws.rs.Path -import jakarta.ws.rs.Produces -import jakarta.ws.rs.QueryParam -import jakarta.ws.rs.core.MediaType -import jakarta.ws.rs.core.Response - -@Path("hello") -class Hello { - - @GET - @Produces(Array(MediaType.TEXT_PLAIN)) - def getMessage(@QueryParam("name") name: String): Response = { - val greeting = if (name == null || name.isEmpty) "Nobody" else name - Response.ok("Hallo " + greeting + "\n").build - } -} diff --git a/examples/payara-micro/src/test/scala/Http.scala b/examples/payara-micro/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/payara-micro/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/payara-micro/src/test/scala/TestSuite.scala b/examples/payara-micro/src/test/scala/TestSuite.scala deleted file mode 100644 index 401f880f..00000000 --- a/examples/payara-micro/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,37 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with BeforeAndAfterAll with Matchers { - - def waitForServer(retries: Int = 30): Unit = - Request( - "GET", - "http://localhost:8080/mycontext/myapp/hello", - Map.empty, - None - ).status match { - case 404 if retries > 0 => - Thread.sleep(1000); waitForServer(retries - 1) - case _ => () - } - - override def beforeAll(): Unit = { - waitForServer() - } - - test("hello") { - Request( - "GET", - "http://localhost:8080/mycontext/myapp/hello?name=James", - Map.empty, - None - ) shouldBe - Response( - status = 200, - Map( - "X-Frame-Options" -> "SAMEORIGIN", - "Content-Type" -> "text/plain" - ), - "Hallo James\n" - ) - } -} diff --git a/examples/sbt-0.x/build.sbt b/examples/sbt-0.x/build.sbt deleted file mode 100644 index 09d9ec84..00000000 --- a/examples/sbt-0.x/build.sbt +++ /dev/null @@ -1,7 +0,0 @@ -scalaVersion := "2.10.2" - -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" - -libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" - -enablePlugins(JettyPlugin) diff --git a/examples/sbt-0.x/project/build.properties b/examples/sbt-0.x/project/build.properties deleted file mode 100644 index 64abd373..00000000 --- a/examples/sbt-0.x/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=0.13.6 diff --git a/examples/sbt-0.x/project/plugins.sbt b/examples/sbt-0.x/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/sbt-0.x/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/sbt-0.x/src/main/scala/GettingStartedServlet.scala b/examples/sbt-0.x/src/main/scala/GettingStartedServlet.scala deleted file mode 100644 index 11416193..00000000 --- a/examples/sbt-0.x/src/main/scala/GettingStartedServlet.scala +++ /dev/null @@ -1,23 +0,0 @@ -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -class GettingStartedServlet extends HttpServlet { - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ): Unit = { - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - val responseBody: String = - """| - | - |

Hello, world!

- | - |""".stripMargin - res.getWriter.write(responseBody) - } -} diff --git a/examples/sbt-0.x/src/main/webapp/WEB-INF/web.xml b/examples/sbt-0.x/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d86c711d..00000000 --- a/examples/sbt-0.x/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - getting started - GettingStartedServlet - - - - getting started - /* - - - diff --git a/examples/sbt-0.x/src/test/scala/Http.scala b/examples/sbt-0.x/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/sbt-0.x/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/sbt-0.x/src/test/scala/TestSuite.scala b/examples/sbt-0.x/src/test/scala/TestSuite.scala deleted file mode 100644 index bf724ac6..00000000 --- a/examples/sbt-0.x/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,21 +0,0 @@ -import org.junit.Assert.assertEquals -import org.junit.Test - -class TestSuite { - - @Test - def getRoot(): Unit = { - assertEquals( - Response( - 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - body = """| - | - |

Hello, world!

- | - |""".stripMargin - ), - Request("GET", "http://localhost:8080/", Map.empty, None) - ) - } -} diff --git a/examples/sbt-1.x/build.sbt b/examples/sbt-1.x/build.sbt deleted file mode 100644 index a2adb4ba..00000000 --- a/examples/sbt-1.x/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -scalaVersion := "3.4.2" - -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" -libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" - -enablePlugins(JettyPlugin) diff --git a/examples/sbt-1.x/project/build.properties b/examples/sbt-1.x/project/build.properties deleted file mode 100644 index ee4c672c..00000000 --- a/examples/sbt-1.x/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.10.1 diff --git a/examples/sbt-1.x/project/plugins.sbt b/examples/sbt-1.x/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/sbt-1.x/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/sbt-1.x/src/main/scala/GettingStartedServlet.scala b/examples/sbt-1.x/src/main/scala/GettingStartedServlet.scala deleted file mode 100644 index 21bf4647..00000000 --- a/examples/sbt-1.x/src/main/scala/GettingStartedServlet.scala +++ /dev/null @@ -1,21 +0,0 @@ -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -class GettingStartedServlet extends HttpServlet: - - override def doGet( - req: HttpServletRequest, - res: HttpServletResponse - ): Unit = - - res.setContentType("text/html") - res.setCharacterEncoding("UTF-8") - - val responseBody: String = - """| - | - |

Hello, world!

- | - |""".stripMargin - res.getWriter.write(responseBody) diff --git a/examples/sbt-1.x/src/main/webapp/WEB-INF/web.xml b/examples/sbt-1.x/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index d86c711d..00000000 --- a/examples/sbt-1.x/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - getting started - GettingStartedServlet - - - - getting started - /* - - - diff --git a/examples/sbt-1.x/src/test/scala/Http.scala b/examples/sbt-1.x/src/test/scala/Http.scala deleted file mode 100644 index 5ab1d57c..00000000 --- a/examples/sbt-1.x/src/test/scala/Http.scala +++ /dev/null @@ -1,58 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request: - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response diff --git a/examples/sbt-1.x/src/test/scala/TestSuite.scala b/examples/sbt-1.x/src/test/scala/TestSuite.scala deleted file mode 100644 index 155133fd..00000000 --- a/examples/sbt-1.x/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,19 +0,0 @@ -import org.junit.Assert.assertEquals -import org.junit.Test - -class TestSuite: - - @Test - def getRoot(): Unit = - assertEquals( - Response( - 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - body = """| - | - |

Hello, world!

- | - |""".stripMargin - ), - Request("GET", "http://localhost:8080/", Map.empty, None) - ) diff --git a/examples/scala-js/README.md b/examples/scala-js/README.md deleted file mode 100644 index 9c39efc8..00000000 --- a/examples/scala-js/README.md +++ /dev/null @@ -1,8 +0,0 @@ -## Testing - -``` -$ sbt fastOptJS Jetty/test -[info] starting server ... -[success] Total time: 2 s, completed Nov 13, 2022 2:16:42 PM -[info] waiting for server to shut down... -``` diff --git a/examples/scala-js/build.sbt b/examples/scala-js/build.sbt deleted file mode 100644 index f78dcb20..00000000 --- a/examples/scala-js/build.sbt +++ /dev/null @@ -1,8 +0,0 @@ -name := "scala-js" - -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" - -enablePlugins(JettyPlugin) -enablePlugins(ScalaJSPlugin) - -scalaJSUseMainModuleInitializer := true diff --git a/examples/scala-js/project/build.properties b/examples/scala-js/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/scala-js/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/scala-js/project/plugins.sbt b/examples/scala-js/project/plugins.sbt deleted file mode 100644 index d2cb566c..00000000 --- a/examples/scala-js/project/plugins.sbt +++ /dev/null @@ -1,2 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") diff --git a/examples/scala-js/scala-js.sbt b/examples/scala-js/scala-js.sbt deleted file mode 100644 index a7fe9679..00000000 --- a/examples/scala-js/scala-js.sbt +++ /dev/null @@ -1,13 +0,0 @@ -enablePlugins(ScalaJSPlugin) - -libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.8.0" - -// Send generated JS to the XWP webapp dir -crossTarget in fastOptJS := (target in webappPrepare).value -crossTarget in fullOptJS := (target in webappPrepare).value - -// Don't append -fastopt/-fullopt to the generated .js filename -artifactPath in (Compile, fastOptJS) := - ((crossTarget in fastOptJS).value / ((moduleName in fastOptJS).value + ".js")) -artifactPath in (Compile, fullOptJS) := - ((crossTarget in fullOptJS).value / ((moduleName in fullOptJS).value + ".js")) diff --git a/examples/scala-js/src/main/scala/HelloWorld.scala b/examples/scala-js/src/main/scala/HelloWorld.scala deleted file mode 100644 index 0b07d599..00000000 --- a/examples/scala-js/src/main/scala/HelloWorld.scala +++ /dev/null @@ -1,17 +0,0 @@ -import org.scalajs.dom -import dom.document - -object HelloWorld { - - def appendPar(targetNode: dom.Node, text: String): Unit = { - val parNode = document.createElement("p") - val textNode = document.createTextNode(text) - parNode.appendChild(textNode) - targetNode.appendChild(parNode) - } - - def main(args: Array[String]): Unit = { - appendPar(document.body, "Hello, world!") - } - -} diff --git a/examples/scala-js/src/main/webapp/WEB-INF/web.xml b/examples/scala-js/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 60b95d4c..00000000 --- a/examples/scala-js/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/examples/scala-js/src/main/webapp/index.html b/examples/scala-js/src/main/webapp/index.html deleted file mode 100644 index 73628d6e..00000000 --- a/examples/scala-js/src/main/webapp/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Scala.js - - - - - diff --git a/examples/scalatra/README.md b/examples/scalatra/README.md deleted file mode 100644 index 17d9c4f9..00000000 --- a/examples/scalatra/README.md +++ /dev/null @@ -1,13 +0,0 @@ -## Testing - -``` -$ sbt test -[info] HelloWorldServletSuite: -[info] - get / -[info] Run completed in 2 seconds, 5 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 3 s, completed Nov 13, 2022 2:18:13 PM -``` diff --git a/examples/scalatra/build.sbt b/examples/scalatra/build.sbt deleted file mode 100644 index 1a56e98a..00000000 --- a/examples/scalatra/build.sbt +++ /dev/null @@ -1,7 +0,0 @@ -libraryDependencies += "org.scalatra" %% "scalatra" % "2.8.4" -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" - -enablePlugins(JettyPlugin) - -Test / fork := true diff --git a/examples/scalatra/project/build.properties b/examples/scalatra/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/scalatra/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/scalatra/project/plugins.sbt b/examples/scalatra/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/scalatra/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/scalatra/src/main/scala/HelloWorldServlet.scala b/examples/scalatra/src/main/scala/HelloWorldServlet.scala deleted file mode 100644 index e194c0b4..00000000 --- a/examples/scalatra/src/main/scala/HelloWorldServlet.scala +++ /dev/null @@ -1,16 +0,0 @@ -import org.scalatra.ScalatraServlet - -class HelloWorldServlet extends ScalatraServlet { - - get("/") { - - contentType = "text/html; charset=utf-8" - - """ - | - |

Hello, world!

- | - |""".stripMargin - } - -} diff --git a/examples/scalatra/src/main/webapp/WEB-INF/web.xml b/examples/scalatra/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 1dcb5a67..00000000 --- a/examples/scalatra/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - hello world - HelloWorldServlet - - - - hello world - /* - - - diff --git a/examples/scalatra/src/test/scala/Http.scala b/examples/scalatra/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/scalatra/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/scalatra/src/test/scala/TestSuite.scala b/examples/scalatra/src/test/scala/TestSuite.scala deleted file mode 100644 index 12a38709..00000000 --- a/examples/scalatra/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,18 +0,0 @@ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class TestSuite extends AnyFunSuite with Matchers { - - test("/") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - 200, - Map("Content-Type" -> "text/html;charset=utf-8"), - body = """| - | - |

Hello, world!

- | - |""".stripMargin - ) - } -} diff --git a/examples/spray/README.md b/examples/spray/README.md deleted file mode 100644 index e637cccc..00000000 --- a/examples/spray/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -[info] starting server ... -[info] TestSuite: -[info] - / -[info] Run completed in 851 milliseconds. -[info] Total number of tests run: 1 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 5 s, completed Nov 13, 2022 2:19:47 PM -[info] waiting for server to shut down... -``` diff --git a/examples/spray/build.sbt b/examples/spray/build.sbt deleted file mode 100644 index a6d726ab..00000000 --- a/examples/spray/build.sbt +++ /dev/null @@ -1,8 +0,0 @@ -scalaVersion := "2.11.12" // spray only goes up to 2.11 - -libraryDependencies += "io.spray" %% "spray-servlet" % "1.3.4" -libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.5.31" -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(JettyPlugin) diff --git a/examples/spray/project/build.properties b/examples/spray/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/spray/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/spray/project/plugins.sbt b/examples/spray/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/spray/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/spray/src/main/resources/application.conf b/examples/spray/src/main/resources/application.conf deleted file mode 100644 index 36cd707a..00000000 --- a/examples/spray/src/main/resources/application.conf +++ /dev/null @@ -1,4 +0,0 @@ -spray.servlet { - boot-class = "Boot" - request-timeout = 6s -} diff --git a/examples/spray/src/main/scala/Boot.scala b/examples/spray/src/main/scala/Boot.scala deleted file mode 100644 index 552c329c..00000000 --- a/examples/spray/src/main/scala/Boot.scala +++ /dev/null @@ -1,10 +0,0 @@ -import akka.actor.ActorSystem -import akka.actor.Props -import spray.servlet.WebBoot - -class Boot extends WebBoot { - - val system = ActorSystem("example") - val serviceActor = system.actorOf(Props[HelloWorldService]) - -} diff --git a/examples/spray/src/main/scala/DemoService.scala b/examples/spray/src/main/scala/DemoService.scala deleted file mode 100644 index b8d4dbb7..00000000 --- a/examples/spray/src/main/scala/DemoService.scala +++ /dev/null @@ -1,11 +0,0 @@ -import akka.actor._ -import spray.http._ -import spray.http.HttpMethods._ - -class HelloWorldService extends Actor with ActorLogging { - - def receive = { case HttpRequest(GET, Uri.Path("/"), _, _, _) => - sender ! HttpResponse(entity = "Hello, world!") - } - -} diff --git a/examples/spray/src/main/webapp/WEB-INF/web.xml b/examples/spray/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index e71aa85e..00000000 --- a/examples/spray/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - spray.servlet.Initializer - - - - SprayConnectorServlet - spray.servlet.Servlet30ConnectorServlet - true - - - - SprayConnectorServlet - /* - - - diff --git a/examples/spray/src/test/scala/Http.scala b/examples/spray/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/spray/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/spray/src/test/scala/TestSuite.scala b/examples/spray/src/test/scala/TestSuite.scala deleted file mode 100644 index ac4050fc..00000000 --- a/examples/spray/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,13 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("/") { - Request("GET", "http://localhost:8080/", Map.empty, None) shouldBe - Response( - status = 200, - Map("Content-Type" -> "text/plain;charset=utf-8"), - "Hello, world!" - ) - } -} diff --git a/examples/zio/README.md b/examples/zio/README.md deleted file mode 100644 index f6c38c84..00000000 --- a/examples/zio/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Testing - -``` -$ sbt Jetty/test -i[info] starting server ... -[info] TestSuite: -[info] - no messages yet -[info] - add a message -[info] - retrieve the message -[info] Run completed in 1 second, 714 milliseconds. -[info] Total number of tests run: 3 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 3, failed 0, canceled 0, ignored 0, pending 0 -[info] All tests passed. -[success] Total time: 5 s, completed Nov 13, 2022 2:21:33 PM -[info] waiting for server to shut down... -``` diff --git a/examples/zio/build.sbt b/examples/zio/build.sbt deleted file mode 100644 index 231ae093..00000000 --- a/examples/zio/build.sbt +++ /dev/null @@ -1,17 +0,0 @@ -libraryDependencies += "dev.zio" %% "zio" % "2.1.9" -libraryDependencies += "javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided" -libraryDependencies += "com.h2database" % "h2" % "2.3.232" -libraryDependencies += "com.jolbox" % "bonecp" % "0.8.0.RELEASE" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.9" % "test" - -enablePlugins(JettyPlugin) - -containerForkOptions := - ForkOptions().withEnvVars { - Map( - "DB_DRIVER" -> "org.h2.Driver", - "DB_URL" -> "jdbc:h2:mem:zio", - "DB_USER" -> "sa", - "DB_PASS" -> "" - ) - } diff --git a/examples/zio/project/build.properties b/examples/zio/project/build.properties deleted file mode 100644 index c8fcab54..00000000 --- a/examples/zio/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.6.2 diff --git a/examples/zio/project/plugins.sbt b/examples/zio/project/plugins.sbt deleted file mode 100644 index 7d370e84..00000000 --- a/examples/zio/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.2.5") diff --git a/examples/zio/src/main/scala/Database.scala b/examples/zio/src/main/scala/Database.scala deleted file mode 100644 index 002ad5af..00000000 --- a/examples/zio/src/main/scala/Database.scala +++ /dev/null @@ -1,67 +0,0 @@ -object Database { - - import com.jolbox.bonecp.BoneCP - import com.jolbox.bonecp.BoneCPConfig - import zio.ZIO - - val connectionPool: BoneCP = { - - Class.forName(sys.env("DB_DRIVER")) - val config = new BoneCPConfig() - config.setJdbcUrl(sys.env("DB_URL")) - config.setUsername(sys.env("DB_USER")) - config.setPassword(sys.env("DB_PASS")) - config.setMinConnectionsPerPartition(5) - config.setMaxConnectionsPerPartition(10) - config.setPartitionCount(1) - new BoneCP(config) - } - - def init: ZIO[JdbcIO, Throwable, Unit] = - JdbcIO.effect { c => - val stmt = c.createStatement() - stmt.executeUpdate { - """|create table if not exists - | guestbook ( id int not null auto_increment - | , name varchar(255) not null - | , message text not null - | , primary key (id) - | ) - |""".stripMargin - } - stmt.close() - } - - def addEntry( - name: String, - message: String - ): ZIO[JdbcIO, Throwable, Unit] = - JdbcIO.effect { c => - val s = "insert into guestbook (name, message) values (?, ?)" - val stmt = c.prepareStatement(s) - stmt.setString(1, name) - stmt.setString(2, message) - stmt.executeUpdate - stmt.close - } - - val getEntries: ZIO[JdbcIO, Throwable, List[(String, String)]] = - JdbcIO.effect { c => - val q = "select name, message from guestbook order by id asc" - val stmt = c.createStatement - val rs = stmt.executeQuery(q) - - def _entries( - acc: List[(String, String)] - ): List[(String, String)] = - if (rs.next()) { - val entry = (rs.getString("name"), rs.getString("message")) - _entries(entry :: acc) - } else { - stmt.close - acc - } - - _entries(Nil) - } -} diff --git a/examples/zio/src/main/scala/HttpClientIO.scala b/examples/zio/src/main/scala/HttpClientIO.scala deleted file mode 100644 index 273118f1..00000000 --- a/examples/zio/src/main/scala/HttpClientIO.scala +++ /dev/null @@ -1,99 +0,0 @@ -object HttpClient { - - import java.net.HttpURLConnection - import java.net.URL - import scala.collection.JavaConverters._ - import scala.io.Source - import zio.Task - import zio.ZIO - - case class Request( - method: String, - url: URL, - headers: Map[String, String], - body: Option[String] - ) - - case class Response( - status: Int, - headers: Map[String, String], - body: String - ) - - def request(r: Request): Task[Response] = - ZIO.attempt { - - val c = r.url.openConnection().asInstanceOf[HttpURLConnection] - - c.setRequestMethod(r.method) - c.setInstanceFollowRedirects(false) - - c.setDoInput(true) - c.setDoOutput(r.body.isDefined) - - r.headers foreach { case (k, v) => c.setRequestProperty(k, v) } - - r.body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val responseStatus = c.getResponseCode - - val responseHeaders = - c.getHeaderFields.asScala.toMap map { case (k, v) => - (k, v.asScala.mkString(",")) - } - - val responseStream = - if (responseStatus < 400) c.getInputStream - else c.getErrorStream - - val responseBody = - if (responseStream != null) { - Source.fromInputStream(responseStream).mkString - } else { - "" - } - - c.disconnect() - - Response( - status = responseStatus, - headers = responseHeaders, - body = responseBody - ) - } - - def post( - url: URL, - headers: Map[String, String] = Map.empty, - body: Option[String] = None - ): Task[Response] = - request(Request("POST", url, headers, body)) - - def get( - url: URL, - headers: Map[String, String] = Map.empty - ): Task[Response] = - request(Request("GET", url, headers, None)) - - def put( - url: URL, - headers: Map[String, String] = Map.empty, - body: Option[String] = None - ): Task[Response] = - request(Request("PUT", url, headers, body)) - - def delete( - url: URL, - headers: Map[String, String] = Map.empty - ): Task[Response] = - request(Request("DELETE", url, headers, None)) - - def head(url: URL, headers: Map[String, String]): Task[Response] = - request(Request("HEAD", url, headers, None)) - - def options(url: URL, headers: Map[String, String]): Task[Response] = - request(Request("OPTIONS", url, headers, None)) - -} diff --git a/examples/zio/src/main/scala/JdbcIO.scala b/examples/zio/src/main/scala/JdbcIO.scala deleted file mode 100644 index 72decbc3..00000000 --- a/examples/zio/src/main/scala/JdbcIO.scala +++ /dev/null @@ -1,35 +0,0 @@ -import java.sql.Connection - -trait JdbcIO { - def connection: Connection -} - -object JdbcIO { - - import java.sql.SQLException - import zio.ZEnvironment - import zio.ZIO - - def effect[A](k: Connection => A): ZIO[JdbcIO, Throwable, A] = - ZIO.environmentWithZIO(env => ZIO.attempt(k(env.get.connection))) - - def transact[R <: JdbcIO, A]( - k: ZIO[R, Throwable, A] - ): ZIO[R, Throwable, A] = - ZIO - .environmentWith({ env: ZEnvironment[JdbcIO] => - env.get.connection.setAutoCommit(false) - env - }) - .flatMap { env => - k.map({ a => - env.get.connection.commit() - env.get.connection.close() - a - }).catchSome({ case x: SQLException => - env.get.connection.rollback() - env.get.connection.close() - ZIO.fail(x) - }) - } -} diff --git a/examples/zio/src/main/scala/ServletIO.scala b/examples/zio/src/main/scala/ServletIO.scala deleted file mode 100644 index f861d19e..00000000 --- a/examples/zio/src/main/scala/ServletIO.scala +++ /dev/null @@ -1,395 +0,0 @@ -object Request { - - import javax.servlet.ServletRequest - import javax.servlet.ServletRequest - import javax.servlet.ServletResponse - import javax.servlet.http.HttpServletRequest - import javax.servlet.http.HttpUpgradeHandler - import scala.collection.JavaConverters._ - import zio.ZEnvironment - import zio.ZIO - - def effect[A]( - k: HttpServletRequest => A - ): ZIO[WithRequest, Throwable, A] = - ZIO.environmentWithZIO(e => ZIO.attempt(k(e.get.request))) - - def effectO[A]( - k: HttpServletRequest => A - ): ZIO[WithRequest, Throwable, Option[A]] = - ZIO.environmentWithZIO(e => ZIO.attempt(Option(k(e.get.request)))) - - def route[A, R <: WithRequest]( - k: PartialFunction[List[String], ZIO[R, Throwable, A]] - ): ZIO[R, Throwable, A] = - for { - method <- getMethod - context <- getServletPath - uri <- getRequestURI - a <- k( - method :: uri - .substring(context.length) - .split("/") - .drop(1) - .toList - ) - } yield a - - val headers: ZIO[WithRequest, Throwable, Map[String, List[String]]] = - getHeaderNames() flatMap { namesO => - ZIO.collectAll { - namesO - .map(_.asScala.toList) - .getOrElse(Nil) - .map({ name => - getHeaders(name) map { valuesO => - ( - name, - valuesO - .map(_.asScala.toList) - .getOrElse(Nil) - ) - } - }) - } map { - _.toMap - } - } - - // ServletRequest methods: - - def getAsyncContext() = - effect(_.getAsyncContext()) - - def getAttribute(name: String) = - effectO(_.getAttribute(name)) - - def getAttributeNames() = - effect(_.getAttributeNames()) - - def getCharacterEncoding() = - effectO(_.getCharacterEncoding()) - - def getContentLength() = - effect(_.getContentLength()) - - def getContentLengthLong() = - effect(_.getContentLengthLong()) - - def getContentType() = - effectO(_.getContentType()) - - def getDispatcherType() = - effect(_.getDispatcherType()) - - def getInputStream() = - effect(_.getInputStream()) - - def getLocalAddr() = - effect(_.getLocalAddr()) - - def getLocalName() = - effect(_.getLocalName()) - - def getLocalPort() = - effect(_.getLocalPort()) - - def getLocale() = - effect(_.getLocale()) - - def getLocales() = - effect(_.getLocales()) - - def getParameter(name: String) = - effectO(_.getParameter(name)) - - def getParameterMap() = - effect(_.getParameterMap()) - - def getParameterNames() = - effect(_.getParameterNames()) - - def getParameterValues(name: String) = - effectO(_.getParameterValues(name)) - - def getProtocol() = - effect(_.getProtocol()) - - def getReader() = - effect(_.getReader()) - - def getRemoteAddr() = - effect(_.getRemoteAddr()) - - def getRemoteHost() = - effect(_.getRemoteHost()) - - def getRemotePort() = - effect(_.getRemotePort()) - - def getRequestDispatcher(path: String) = - effectO(_.getRequestDispatcher(path)) - - def getScheme() = - effect(_.getScheme()) - - def getServerName() = - effect(_.getServerName()) - - def getServerPort() = - effect(_.getServerPort()) - - def getServletContext() = - effect(_.getServletContext()) - - def isAsyncStarted() = - effect(_.isAsyncStarted()) - - def isAsyncSupported() = - effect(_.isAsyncSupported()) - - def isSecure() = - effect(_.isSecure()) - - def removeAttribute(name: String) = - effect(_.removeAttribute(name)) - - def setAttribute(name: String, o: Object) = - effect(_.setAttribute(name, o)) - - def setCharacterEncoding(env: String) = - effect(_.setCharacterEncoding(env)) - - def startAsync() = - effect(_.startAsync()) - - def startAsync( - servletRequest: ServletRequest, - servletResponse: ServletResponse - ) = - effect(_.startAsync(servletRequest, servletResponse)) - - // HttpServletRequest methods: - - def authenticate - : ZIO[WithRequest with WithResponse, Throwable, Boolean] = - ZIO.environmentWithZIO { req: ZEnvironment[WithRequest] => - ZIO.environmentWithZIO { res: ZEnvironment[WithResponse] => - ZIO.attempt(req.get.request.authenticate(res.get.response)) - } - } - - def changeSessionId() = - effect(_.changeSessionId()) - - def getAuthType() = - effectO(_.getAuthType()) - - def getContextPath() = - effect(_.getContextPath()) - - def getCookies() = - effectO(_.getCookies()) - - def getDateHeader(name: String) = - effect(_.getDateHeader(name)) - - def getHeader(name: String) = - effectO(_.getHeader(name)) - - def getHeaderNames() = - effectO(_.getHeaderNames()) - - def getHeaders(name: String) = - effectO(_.getHeaders(name)) - - def getIntHeader(name: String) = - effect(_.getIntHeader(name)) - - def getMethod() = - effect(_.getMethod()) - - def getPart(name: String) = - effectO(_.getPart(name)) - - def getParts() = - effect(_.getParts()) - - def getPathInfo() = - effectO(_.getPathInfo()) - - def getPathTranslated() = - effectO(_.getPathTranslated()) - - def getQueryString() = - effectO(_.getQueryString()) - - def getRemoteUser() = - effectO(_.getRemoteUser()) - - def getRequestURI() = - effect(_.getRequestURI()) - - def getRequestURL() = - effect(_.getRequestURL()) - - def getRequestedSessionId() = - effectO(_.getRequestedSessionId()) - - def getServletPath() = - effect(_.getServletPath()) - - def getSession() = - effect(_.getSession()) - - def getSession(create: Boolean) = - effectO(_.getSession(create)) - - def getUserPrincipal() = - effectO(_.getUserPrincipal()) - - def isRequestedSessionIdFromCookie() = - effect(_.isRequestedSessionIdFromCookie()) - - def isRequestedSessionIdFromURL() = - effect(_.isRequestedSessionIdFromURL()) - - def isRequestedSessionIdValid() = - effect(_.isRequestedSessionIdValid()) - - def isUserInRole(role: String) = - effect(_.isUserInRole(role)) - - def login(username: String, password: String) = - effect(_.login(username, password)) - - def logout() = - effect(_.logout()) - - def upgrade[T <: HttpUpgradeHandler](handlerClass: Class[T]) = - effect(_.upgrade(handlerClass)) -} - -object Response { - - import java.util.Locale - import javax.servlet.http.Cookie - import javax.servlet.http.HttpServletResponse - import zio.ZIO - - def effect[A]( - k: HttpServletResponse => A - ): ZIO[WithResponse, Throwable, A] = - ZIO.environmentWithZIO(e => ZIO.attempt(k(e.get.response))) - - def effectO[A]( - k: HttpServletResponse => A - ): ZIO[WithResponse, Throwable, Option[A]] = - ZIO.environmentWithZIO(e => ZIO.attempt(Option(k(e.get.response)))) - - // ServletResponse methods: - - def flushBuffer() = - effect(_.flushBuffer()) - - def getBufferSize() = - effect(_.getBufferSize()) - - def getCharacterEncoding() = - effect(_.getCharacterEncoding()) - - def getContentType() = - effectO(_.getContentType()) - - def getLocale() = - effect(_.getLocale()) - - def getOutputStream() = - effect(_.getOutputStream()) - - def getWriter() = - effect(_.getWriter()) - - def isCommitted() = - effect(_.isCommitted()) - - def reset() = - effect(_.reset()) - - def resetBuffer() = - effect(_.resetBuffer()) - - def setBufferSize(size: Int) = - effect(_.setBufferSize(size)) - - def setCharacterEncoding(charset: String) = - effect(_.setCharacterEncoding(charset)) - - def setContentLength(len: Int) = - effect(_.setContentLength(len)) - - def setContentLengthLong(len: Long) = - effect(_.setContentLengthLong(len)) - - def setContentType(contentType: String) = - effect(_.setContentType(contentType)) - - def setLocale(loc: Locale) = - effect(_.setLocale(loc)) - - // HttpServletResponse methods: - - def addCookie(cookie: Cookie) = - effect(_.addCookie(cookie)) - - def addDateHeader(name: String, date: Long) = - effect(_.addDateHeader(name, date)) - - def addHeader(name: String, value: String) = - effect(_.addHeader(name, value)) - - def addIntHeader(name: String, value: Int) = - effect(_.addIntHeader(name, value)) - - def containsHeader(name: String) = - effect(_.containsHeader(name)) - - def encodeRedirectURL(url: String) = - effect(_.encodeRedirectURL(url)) - - def encodeURL(url: String) = - effect(_.encodeURL(url)) - - def getHeader(name: String) = - effectO(_.getHeader(name)) - - def getHeaderNames() = - effect(_.getHeaderNames()) - - def getHeaders(name: String) = - effect(_.getHeaders(name)) - - def getStatus() = - effect(_.getStatus()) - - def sendError(sc: Int) = - effect(_.sendError(sc)) - - def sendError(sc: Int, msg: String) = - effect(_.sendError(sc, msg)) - - def sendRedirect(location: String) = - effect(_.sendRedirect(location)) - - def setDateHeader(name: String, date: Long) = - effect(_.setDateHeader(name, date)) - - def setHeader(name: String, value: String) = - effect(_.setHeader(name, value)) - - def setIntHeader(name: String, value: Int) = - effect(_.setIntHeader(name, value)) - - def setStatus(sc: Int) = - effect(_.setStatus(sc)) -} diff --git a/examples/zio/src/main/scala/ZioServlet.scala b/examples/zio/src/main/scala/ZioServlet.scala deleted file mode 100644 index 838083ff..00000000 --- a/examples/zio/src/main/scala/ZioServlet.scala +++ /dev/null @@ -1,109 +0,0 @@ -import javax.servlet.http.HttpServlet -import zio.ZIO - -object Services { - - val getMessages: ZIO[ - WithRequest with WithResponse with JdbcIO, - Throwable, - Unit - ] = - for { - _ <- Response.setContentType("text/html;charset=UTF-8") - entries <- Database.getEntries - writer <- Response.getWriter - _ = writer.write("
    \n") - _ = entries map { case (name, message) => - writer.write(s"
  • ${name}: ${message}
  • \n") - } - _ = writer.write("
\n") - } yield () - - val addMessage: ZIO[ - WithRequest with WithResponse with JdbcIO, - Throwable, - Unit - ] = - for { - nameO <- Request.getParameter("name") - messageO <- Request.getParameter("message") - _ <- (nameO, messageO) match { - case (Some(name), Some(message)) => - for { - _ <- Database.addEntry(name, message) - _ <- Response.sendRedirect("/") - } yield () - case _ => - for { - _ <- Response.setStatus(400) - } yield () - } - - } yield () -} - -class ZioServlet extends HttpServlet { - - import javax.servlet.http.HttpServletRequest - import javax.servlet.http.HttpServletResponse - import zio.Runtime - import zio.Unsafe - import zio.ZLayer - - def unsafeRun[A]( - req: HttpServletRequest, - res: HttpServletResponse - )( - k: ZIO[WithRequest with WithResponse with JdbcIO, Throwable, A] - ): Either[Throwable, A] = { - - val requestEnv = new WithRequest { val request = req } - val responseEnv = new WithResponse { val response = res } - val jdbcEnv = - new JdbcIO { - Class.forName("org.h2.Driver") - val connection = Database.connectionPool.getConnection - } - - Unsafe.unsafe { implicit u: Unsafe => - Runtime.default.unsafe - .run( - JdbcIO - .transact(k) - .map(a => Right(a)) - .catchAll { t => - ZIO.succeed(Left[Throwable, A](t)) - } - .provide( - ZLayer.succeed(requestEnv) ++ - ZLayer.succeed(responseEnv) ++ - ZLayer.succeed(jdbcEnv) - ) - ) - .getOrThrowFiberFailure() - } - } - - override def init: Unit = { - unsafeRun(null, null)(Database.init) - } - - override def service( - req: HttpServletRequest, - res: HttpServletResponse - ): Unit = { - try { - unsafeRun(req, res) { - Request.route { - case "GET" :: Nil => Services.getMessages - case "POST" :: Nil => Services.addMessage - case _ => Response.setStatus(404) - } - } - } catch { - case t: Throwable => - t.printStackTrace - res.setStatus(500) - } - } -} diff --git a/examples/zio/src/main/scala/package.scala b/examples/zio/src/main/scala/package.scala deleted file mode 100644 index 298ea354..00000000 --- a/examples/zio/src/main/scala/package.scala +++ /dev/null @@ -1,20 +0,0 @@ -trait WithConnection { - def connection: java.sql.Connection -} - -trait WithRequest { - def request: javax.servlet.http.HttpServletRequest -} - -trait WithResponse { - def response: javax.servlet.http.HttpServletResponse -} - -object `package` { - - import zio.ZIO - - type DB[A] = ZIO[WithConnection, Throwable, A] - type Req[A] = ZIO[WithRequest, Throwable, A] - type Res[A] = ZIO[WithResponse, Throwable, A] -} diff --git a/examples/zio/src/main/webapp/WEB-INF/web.xml b/examples/zio/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index cfbb3811..00000000 --- a/examples/zio/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - ZioServlet - ZioServlet - true - - - - ZioServlet - /* - - - diff --git a/examples/zio/src/test/scala/Http.scala b/examples/zio/src/test/scala/Http.scala deleted file mode 100644 index b922e744..00000000 --- a/examples/zio/src/test/scala/Http.scala +++ /dev/null @@ -1,60 +0,0 @@ -import java.net._ -import scala.collection.JavaConverters._ -import scala.io.Source - -case class Response( - status: Int, - headers: Map[String, String], - body: String -) - -object Request { - - def apply( - method: String, - url: String, - headers: Map[String, String], - body: Option[String] - ): Response = { - - val c = - new URL(url) - .openConnection() - .asInstanceOf[HttpURLConnection] - - c.setInstanceFollowRedirects(false) - c.setRequestMethod(method) - c.setDoInput(true) - c.setDoOutput(body.isDefined) - - headers foreach { case (k, v) => - c.setRequestProperty(k, v) - } - - body foreach { b => - c.getOutputStream.write(b.getBytes("UTF-8")) - } - - val response = - Response( - status = c.getResponseCode(), - headers = c - .getHeaderFields() - .asScala - .filter({ case (k, _) => k != null }) - .map({ case (k, v) => (k, v.asScala.mkString(",")) }) - .toMap - "Date" - "Content-Length" - "Server", - body = Source.fromInputStream { - if (c.getResponseCode() < 400) { - c.getInputStream - } else { - c.getErrorStream - } - }.mkString - ) - - c.disconnect() - - response - } -} diff --git a/examples/zio/src/test/scala/TestSuite.scala b/examples/zio/src/test/scala/TestSuite.scala deleted file mode 100644 index 1f65c5d0..00000000 --- a/examples/zio/src/test/scala/TestSuite.scala +++ /dev/null @@ -1,54 +0,0 @@ -import org.scalatest._ - -class TestSuite extends FunSuite with Matchers { - - test("no messages yet") { - Request( - method = "GET", - url = "http://localhost:8080/", - headers = Map.empty, - body = None - ) shouldBe { - Response( - status = 200, - headers = Map("Content-Type" -> "text/html;charset=utf-8"), - body = """|
    - |
- |""".stripMargin - ) - } - } - - test("add a message") { - Request( - method = "POST", - url = "http://localhost:8080/", - headers = Map.empty, - body = Some("name=jdoe&message=Howdy!") - ) shouldBe { - Response( - status = 302, - headers = Map("Location" -> "http://localhost:8080/"), - body = "" - ) - } - } - - test("retrieve the message") { - Request( - method = "GET", - url = "http://localhost:8080/", - headers = Map.empty, - body = None - ) shouldBe { - Response( - status = 200, - headers = Map("Content-Type" -> "text/html;charset=utf-8"), - body = """|
    - |
  • jdoe: Howdy!
  • - |
- |""".stripMargin - ) - } - } -} diff --git a/shell.nix b/shell.nix index 0d21993c..3d4d62d0 100644 --- a/shell.nix +++ b/shell.nix @@ -1,6 +1,6 @@ { pkgs ? import {} }: let - jdk = pkgs.jdk8; + jdk = pkgs.jdk11; in pkgs.mkShell { nativeBuildInputs = [ diff --git a/src/template/build.sbt b/src/template/build.sbt index 54b6c084..a1beeb0a 100644 --- a/src/template/build.sbt +++ b/src/template/build.sbt @@ -2,7 +2,7 @@ libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.4" libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test" libraryDependencies += "com.h2database" % "h2" % "2.2.224" -libraryDependencies += "jakarta.servlet" % "jakarta.servlet-api" % "6.0.0" % "provided" +libraryDependencies += "jakarta.servlet" % "jakarta.servlet-api" % "6.0.0" % Provided libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5" libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.5.8" diff --git a/src/template/project/plugins.sbt b/src/template/project/plugins.sbt index bc97c43d..da29e13c 100644 --- a/src/template/project/plugins.sbt +++ b/src/template/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "0.1.0-SNAPSHOT") +addSbtPlugin("com.earldouglas" % "sbt-war" % "0.1.0-SNAPSHOT") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1")