Gradle 8.12 で JSON を返すだけのサーバーを書く。
$ gradle --version
Welcome to Gradle 8.12!
gradle init する:
$ gradle init
Starting a Gradle Daemon (subsequent builds will be faster)
Select type of build to generate:
1: Application
2: Library
3: Gradle plugin
4: Basic (build structure only)
Enter selection (default: Application) [1..4] 1
Select implementation language:
1: Java
2: Kotlin
3: Groovy
4: Scala
5: C++
6: Swift
Enter selection (default: Java) [1..6] 2
Enter target Java version (min: 7, default: 21): 17
Project name (default: my-ktor):
Select application structure:
1: Single application project
2: Application and library project
Enter selection (default: Single application project) [1..2] 1
Select build script DSL:
1: Kotlin
2: Groovy
Enter selection (default: Kotlin) [1..2] 1
Select test framework:
1: kotlin.test
2: JUnit Jupiter
Enter selection (default: kotlin.test) [1..2] 1
Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no] yes
生成された内容:
$ tree .
.
├── app
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ │ ├── kotlin
│ │ │ └── org
│ │ │ └── example
│ │ │ └── App.kt
│ │ └── resources
│ └── test
│ ├── kotlin
│ │ └── org
│ │ └── example
│ │ └── AppTest.kt
│ └── resources
├── gradle
│ ├── libs.versions.toml
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
不要ファイル/ディレクトリの削除:
必要なファイルを追加:
追加したファイルにはそれぞれ次のコードを書く。
application.conf
ktor {
deployment {
port = 8080
}
application {
modules = [ org.example.ApplicationKt.main ]
}
}
Application.kt
@file:Suppress("InvalidPackageDeclaration")
package org.example
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.http.ContentType
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class Pokemon(val id: Int, val name: String)
fun Application.main() {
val defaultPokemon = Pokemon(0, "Pikachu")
val pokemonStorage = mutableListOf<Pokemon>()
pokemonStorage.addAll(
arrayOf(
Pokemon(1, "Squirtle"),
Pokemon(2, "Charamander"),
Pokemon(3, "Metapod"),
)
)
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
}
routing {
get("/") {
val pokemons: List<Pokemon> = pokemonStorage.toList()
call.respond(pokemons)
}
get("/{id}") {
val id = call.parameters["id"]
val pokemon: Pokemon? = pokemonStorage.find { it.id == id!!.toInt() }
if( pokemon!=null ){
call.respond(pokemon)
} else {
call.respond(defaultPokemon)
}
}
}
}
さらに既存のファイルを書きかえる:
app/build.gradle.kt
plugins {
alias(libs.plugins.kotlin.jvm)
application
alias(libs.plugins.kotlin.plugin.serialization)
}
repositories {
mavenCentral()
}
dependencies {
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.netty)
implementation(libs.ktor.server.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
application {
mainClass.set("io.ktor.server.netty.EngineMain")
}
gradle/lib.versions.toml
[versions]
ktor-version = "3.0.3"
kotlin-version = "2.1.0"
[libraries]
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor-version" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor-version" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor-version" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor-version" }
ktor-serialization-kotlinx-xml = { module = "io.ktor:ktor-serialization-kotlinx-xml", version.ref = "ktor-version" }
ktor-serialization-kotlinx-cbor = { module = "io.ktor:ktor-serialization-kotlinx-cbor", version.ref = "ktor-version" }
ktor-serialization-kotlinx-protobuf = { module = "io.ktor:ktor-serialization-kotlinx-protobuf", version.ref = "ktor-version" }
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-version" }
kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-version" }
この段階でのディレクトリ構成を確認:
$ tree .
.
├── app
│ ├── build.gradle.kts
│ └── src
│ └── main
│ ├── kotlin
│ │ └── Application.kt
│ └── resources
│ └── application.conf
├── gradle
│ ├── libs.versions.toml
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── settings.gradle.kts
かなりすっきりしました。
これで起動して作動を確かめます。
$ ./gradlew run
起動したら curl で作動を確かめます。
$ curl -X GET http://localhost:8080/
[
{
"id": 1,
"name": "Squirtle"
},
{
"id": 2,
"name": "Charamander"
},
{
"id": 3,
"name": "Metapod"
}
]
java -jar fat.jar で起動できるようにします。
gradle/lib.versions.toml の plugins セクションに ktor の行を追加:
[plugins]
ktor = { id = "io.ktor.plugin", version.ref = "ktor-version" }
...
さらに app/build.gradle.kts に fat jar をつくるための設定を追加:
plugins {
alias(libs.plugins.ktor)
...
}
...
ktor {
fatJar {
archiveFileName.set("fat.jar")
}
}
tasks に buildFatJar や runFatJar が追加されたことを確認:
$ ./gradlew tasks | grep Fat
buildFatJar - Builds a combined JAR of project and runtime dependencies.
runFatJar - Builds a combined JAR of project and runtime dependencies and runs it.
では fat jar を生成:
$ ./gradlew buildFatJar
$ find . -name fat.jar
./app/build/install/app-shadow/lib/fat.jar
./app/build/libs/fat.jar
実行します:
$ java -jar app/build/libs/fat.jar
id が 1 のポケモン情報を取得:
$ curl -X GET http://localhost:8080/1
{
"id": 1,
"name": "Squirtle"
}
できました。
以上です。