Gradle + Gretty + Kotlin で Servlet お手軽実装

# はじめに
久しぶりにServletを書こうと思ったところ、APサーバ（TomcatやJetty）用意するの面倒だなと。。。Spring-bootのように簡単に組み込みエンジンが動いてくれたら良いなと思った。
Gradleから実行するのが最も簡単かなと思い、Cargoを思い出したがなんか設定が面倒。自分でmain関数からembeded jettyとか使ってもいいかなと思ったが、良いものを発見。

Gretty documentation https://gretty-gradle-plugin.github.io/gretty-doc/index.html

Grettyを使った、お手軽Java EE開発！

## プロジェクトを作成
最近はKotlinが主流だし、折角なのでKotlinで書いてみようと思います。

まずは、プロジェクト用のディレクトリを作成し、以下のコマンドを実行。

```
PS C:\Users\user\Documents\temp\servlet-sample> gradle init --type=kotlin-application

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Kotlin) [1..2] 2

Project name (default: servlet-sample):
Source package (default: servlet.sample): com.sample

BUILD SUCCESSFUL in 19s
2 actionable tasks: 2 executed
PS C:\Users\user\Documents\temp\servlet-sample> ls


    Directory: C:\Users\user\Documents\temp\servlet-sample

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2020/06/24    16:20                .gradle
d----          2020/06/24    16:20                gradle
d----          2020/06/24    16:20                src
-a---          2020/06/24    16:20            160 .gitattributes
-a---          2020/06/24    16:20            108 .gitignore
-a---          2020/06/24    16:20           1102 build.gradle.kts
-a---          2020/06/24    16:20           5770 gradlew
-a---          2020/06/24    16:20           3058 gradlew.bat
-a---          2020/06/24    16:20            373 settings.gradle.kts
```

## build.gradle　の修正
JavaEEの依存関係とGrettyの設定を行います

``` build.gradle.kts
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Kotlin application project to get you started.
 */

plugins {
    // Apply the Kotlin JVM plugin to add support for Kotlin.
    id("org.jetbrains.kotlin.jvm") version "1.3.71"
    id("org.gretty") version "3.0.3"
    war
    // Apply the application plugin to add support for building a CLI application.
    //application
}

val moreLibs = configurations.create("moreLibs")
repositories {
    // Use jcenter for resolving dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}


dependencies {

    // Align versions of all Kotlin components
    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))

    // Use the Kotlin JDK 8 standard library.
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    // Use the Kotlin test library.
    testImplementation("org.jetbrains.kotlin:kotlin-test")

    // Use the Kotlin JUnit integration.
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")

    compileOnly("javax.servlet:javax.servlet-api:4.0.1")
}

//application {
//    // Define the main class for the application.
//    mainClassName = "com.sample.AppKt"
//}
```

plugin から Application を削除して war を追加します。
次に、Grettyのプラグインを追加します。

```
plugins {
    // Apply the Kotlin JVM plugin to add support for Kotlin.
    id("org.jetbrains.kotlin.jvm") version "1.3.71"
    id("org.gretty") version "3.0.3"
    war
    // Apply the application plugin to add support for building a CLI application.
    //application
}
```

java EE の依存関係を追加します。昔はProvidedなモジュールは providedCompile を指定していましたが、最新のGradleでは compileOnly になったので注意が必要です。

```
dependencies {

    // Align versions of all Kotlin components
    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))

    // Use the Kotlin JDK 8 standard library.
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

    // Use the Kotlin test library.
    testImplementation("org.jetbrains.kotlin:kotlin-test")

    // Use the Kotlin JUnit integration.
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")

    compileOnly("javax.servlet:javax.servlet-api:4.0.1")
}
```

## ソースコード作成
簡単なサーブレットを書いてみます。

``` HelloWorld.kt
package com.sample

import javax.servlet.annotation.WebServlet
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@WebServlet(name = "hello",urlPatterns = arrayOf("/hello"))
class HelloWorld : HttpServlet() {
    override fun doGet(req: HttpServletRequest?, resp: HttpServletResponse?) {
        val text = """
            {"hello":"world"}
        """.trimIndent()
        resp?.setContentType("application/json")
        resp?.getWriter()?.println(text)
    }
}
```

## 実行

```
C:\Users\user\Documents\temp\servlet-sample>gradlew appRun
Starting a Gradle Daemon (subsequent builds will be faster)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/user/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.10/e02047a2de591492cb19e82c9a72bd9d24b25235/groovy-2.5.10.jar) t
o method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
11:52:08 WARN  Ignoring deprecated socket close linger time
11:52:18 INFO  Jetty 9.4.24.v20191120 started and listening on port 8080
11:52:18 INFO  servlet-sample runs at:
11:52:18 INFO    http://localhost:8080/servlet-sample

> Task :appRun
Press any key to stop the server.
<===========--> 88% EXECUTING [1m 1s]
> :appRun
```

```
PS C:\Users\user> curl -i -X GET 'http://localhost:8080/servlet-sample/hello'
HTTP/1.1 200 OK
Date: Fri, 26 Jun 2020 02:54:27 GMT
Content-Type: application/json
Content-Length: 19
Server: Jetty(9.4.24.v20191120)

{"hello":"world"}

```

#まとめ
Gradleにプラグインを追加するだけで、簡単に実行する事ができました。ディフォルトではJettyのようですが、Tomcatも選べます。Gradleの設定でディフォルトのコンテナを設定したり、ポートの変更、HotDeployの設定など行う事ができます。
また、提供されているタスクを使ってTomcatを動かすことも可能です。

```
gradlew tomcatRun ← Tomcat実行
gradlew jettyRun ← Jetty実行
```
なんか、Springbootも動かせるみたいだけど、bootならこれいらんよね。。。