diff --git a/codeSnippets/settings.gradle.kts b/codeSnippets/settings.gradle.kts index 7cc08205f..675bc3df3 100644 --- a/codeSnippets/settings.gradle.kts +++ b/codeSnippets/settings.gradle.kts @@ -120,7 +120,6 @@ module("snippets", "deployment-ktor-plugin") module("snippets", "tutorial-website-static") module("snippets", "server-websockets-sharedflow") module("snippets", "tutorial-client-get-started") -module("snippets", "tutorial-server-get-started") module("snippets", "tutorial-server-web-application") module("snippets", "server-websockets-serialization") module("snippets", "client-websockets-serialization") diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/pom.xml b/codeSnippets/snippets/tutorial-server-get-started-maven/pom.xml index 6bf7a3f04..294a2f815 100644 --- a/codeSnippets/snippets/tutorial-server-get-started-maven/pom.xml +++ b/codeSnippets/snippets/tutorial-server-get-started-maven/pom.xml @@ -8,14 +8,14 @@ tutorial-server-get-started-maven tutorial-server-get-started-maven - 3.3.1 official 2.2.20 - 1.5.18 - 2.0.12 + 3.3.1 + 1.4.14 + 2.0.9 UTF-8 true - com.example.ApplicationKt + io.ktor.server.netty.EngineMain @@ -35,6 +35,11 @@ logback-classic ${logback_version} + + io.ktor + ktor-server-status-pages-jvm + ${ktor_version} + org.slf4j slf4j-api @@ -111,7 +116,7 @@ org.apache.maven.plugins maven-assembly-plugin - 2.6 + 3.7.1 jar-with-dependencies diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/Application.kt b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/Application.kt new file mode 100644 index 000000000..549d1edb5 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/Application.kt @@ -0,0 +1,11 @@ +package com.example + +import io.ktor.server.application.* + +fun main(args: Array) { + io.ktor.server.netty.EngineMain.main(args) +} + +fun Application.module() { + configureRouting() +} \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/com/example/plugins/Routing.kt b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/Routing.kt similarity index 80% rename from codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/com/example/plugins/Routing.kt rename to codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/Routing.kt index 415949833..efc7bf8dc 100644 --- a/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/com/example/plugins/Routing.kt +++ b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/Routing.kt @@ -1,12 +1,11 @@ -package com.example.plugins +package com.example -import io.ktor.http.* +import io.ktor.http.ContentType import io.ktor.server.application.* -import io.ktor.server.http.content.* +import io.ktor.server.http.content.staticResources +import io.ktor.server.plugins.statuspages.StatusPages import io.ktor.server.response.* import io.ktor.server.routing.* -import io.ktor.server.routing.get -import io.ktor.server.plugins.statuspages.* fun Application.configureRouting() { install(StatusPages) { @@ -21,11 +20,14 @@ fun Application.configureRouting() { call.respondText("Hello World!") } + // ... + get("/test1") { val text = "

Hello From Ktor

" val type = ContentType.parse("text/html") call.respondText(text, type) } + get("/error-test") { throw IllegalStateException("Too Busy") } diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/com/example/Application.kt b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/com/example/Application.kt deleted file mode 100644 index 5f5552d9c..000000000 --- a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/com/example/Application.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.example - -import com.example.plugins.* -import io.ktor.server.application.* -import io.ktor.server.engine.* -import io.ktor.server.netty.* - -fun main() { - embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module) - .start(wait = true) -} - -fun Application.module() { - configureRouting() -} diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/com/example/plugins/Routing.kt b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/com/example/plugins/Routing.kt deleted file mode 100644 index 293b71d34..000000000 --- a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/kotlin/com/example/plugins/Routing.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.plugins - -import io.ktor.server.application.* -import io.ktor.server.response.* -import io.ktor.server.routing.* - -fun Application.configureRouting() { - routing { - get("/") { - call.respondText("Hello World!") - } - } -} diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/application.conf b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/resources/application.conf similarity index 100% rename from codeSnippets/snippets/tutorial-server-get-started/src/main/resources/application.conf rename to codeSnippets/snippets/tutorial-server-get-started-maven/src/main/resources/application.conf diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/resources/logback.xml b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/resources/logback.xml index 3e11d7811..aadef5d5b 100644 --- a/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/resources/logback.xml +++ b/codeSnippets/snippets/tutorial-server-get-started-maven/src/main/resources/logback.xml @@ -4,7 +4,7 @@ %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/src/test/kotlin/ApplicationTest.kt b/codeSnippets/snippets/tutorial-server-get-started-maven/src/test/kotlin/ApplicationTest.kt new file mode 100644 index 000000000..c5e7fe506 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started-maven/src/test/kotlin/ApplicationTest.kt @@ -0,0 +1,37 @@ +package com.example + +import io.ktor.client.request.get +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType +import io.ktor.server.testing.testApplication +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals + +class ApplicationTest { + + @Test + fun testRoot() = testApplication { + application { + module() + } + val response = client.get("/") + + assertEquals(HttpStatusCode.OK, response.status) + assertEquals("Hello World!", response.bodyAsText()) + } + + @Test + fun testNewEndpoint() = testApplication { + application { + module() + } + + val response = client.get("/test1") + + assertEquals(HttpStatusCode.OK, response.status) + assertEquals("html", response.contentType()?.contentSubtype) + assertContains(response.bodyAsText(), "Hello From Ktor") + } +} \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started-maven/src/test/kotlin/com/example/ApplicationTest.kt b/codeSnippets/snippets/tutorial-server-get-started-maven/src/test/kotlin/com/example/ApplicationTest.kt deleted file mode 100644 index b4e0c549c..000000000 --- a/codeSnippets/snippets/tutorial-server-get-started-maven/src/test/kotlin/com/example/ApplicationTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.example - -import com.example.plugins.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.testing.* -import kotlin.test.* - -class ApplicationTest { - @Test - fun testRoot() = testApplication { - application { - configureRouting() - } - client.get("/").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals("Hello World!", bodyAsText()) - } - } -} diff --git a/codeSnippets/snippets/tutorial-server-get-started/README.md b/codeSnippets/snippets/tutorial-server-get-started/README.md index 987e38329..16edc58ec 100644 --- a/codeSnippets/snippets/tutorial-server-get-started/README.md +++ b/codeSnippets/snippets/tutorial-server-get-started/README.md @@ -1,17 +1,15 @@ # Getting started with a Ktor Server -A sample project created in +A sample standalone project created in the [Create, open and run a new Ktor project](https://ktor.io/docs/server-create-a-new-project.html) tutorial. -> This sample is part of the [codeSnippets](../../README.md) Gradle project. - ## Run -To run the sample, execute the following command in a repository's root directory: +To run the sample, execute the following command in the project's root directory: ```bash -./gradlew :tutorial-server-get-started:run +./gradlew run ``` Navigate to [http://0.0.0.0:8080/](http://0.0.0.0:8080/) to see the output. diff --git a/codeSnippets/snippets/tutorial-server-get-started/build.gradle.kts b/codeSnippets/snippets/tutorial-server-get-started/build.gradle.kts index 96bda3cc2..66a118c77 100644 --- a/codeSnippets/snippets/tutorial-server-get-started/build.gradle.kts +++ b/codeSnippets/snippets/tutorial-server-get-started/build.gradle.kts @@ -1,28 +1,23 @@ -val ktor_version: String by project -val kotlin_version: String by project -val logback_version: String by project - plugins { - application - kotlin("jvm") - id("io.ktor.plugin") version "3.3.1" + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.ktor) } -application { - mainClass.set("io.ktor.server.netty.EngineMain") -} +group = "com.example" +version = "0.0.1" -repositories { - mavenCentral() - maven { url = uri("https://maven.pkg.jetbrains.space/public/p/ktor/eap") } +application { + mainClass = "io.ktor.server.netty.EngineMain" } dependencies { - implementation("io.ktor:ktor-server-status-pages:$ktor_version") - implementation("io.ktor:ktor-server-core-jvm") - implementation("io.ktor:ktor-server-netty-jvm") - implementation("ch.qos.logback:logback-classic:$logback_version") - implementation("io.ktor:ktor-server-config-yaml:$ktor_version") - testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") -} \ No newline at end of file + // Added new dependency + implementation(libs.ktor.server.status.pages) + // Existing dependencies + implementation(libs.ktor.server.core) + implementation(libs.ktor.server.netty) + implementation(libs.logback.classic) + implementation(libs.ktor.server.config.yFaml) + testImplementation(libs.ktor.server.test.host) + testImplementation(libs.kotlin.test.junit) +} diff --git a/codeSnippets/snippets/tutorial-server-get-started/gradle.properties b/codeSnippets/snippets/tutorial-server-get-started/gradle.properties new file mode 100644 index 000000000..29e08e8ca --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/gradle/libs.versions.toml b/codeSnippets/snippets/tutorial-server-get-started/gradle/libs.versions.toml new file mode 100644 index 000000000..8e53a5983 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/gradle/libs.versions.toml @@ -0,0 +1,17 @@ +[versions] +kotlin = "2.2.20" +ktor = "3.3.0" +logback = "1.4.14" + +[libraries] +ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" } +ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" } +ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" } +logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } +ktor-server-config-yaml = { module = "io.ktor:ktor-server-config-yaml", version.ref = "ktor" } +ktor-server-test-host = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" } +kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } + +[plugins] +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +ktor = { id = "io.ktor.plugin", version.ref = "ktor" } \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/gradle/wrapper/gradle-wrapper.jar b/codeSnippets/snippets/tutorial-server-get-started/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..1b33c55ba Binary files /dev/null and b/codeSnippets/snippets/tutorial-server-get-started/gradle/wrapper/gradle-wrapper.jar differ diff --git a/codeSnippets/snippets/tutorial-server-get-started/gradle/wrapper/gradle-wrapper.properties b/codeSnippets/snippets/tutorial-server-get-started/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..42f0071e3 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/gradlew b/codeSnippets/snippets/tutorial-server-get-started/gradlew new file mode 100755 index 000000000..23d15a936 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/codeSnippets/snippets/tutorial-server-get-started/gradlew.bat b/codeSnippets/snippets/tutorial-server-get-started/gradlew.bat new file mode 100644 index 000000000..5eed7ee84 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/codeSnippets/snippets/tutorial-server-get-started/settings.gradle.kts b/codeSnippets/snippets/tutorial-server-get-started/settings.gradle.kts new file mode 100644 index 000000000..6e62c7df3 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/settings.gradle.kts @@ -0,0 +1,7 @@ +rootProject.name = "ktor-sample" + +dependencyResolutionManagement { + repositories { + mavenCentral() + } +} \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/Application.kt b/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/Application.kt new file mode 100644 index 000000000..549d1edb5 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/Application.kt @@ -0,0 +1,11 @@ +package com.example + +import io.ktor.server.application.* + +fun main(args: Array) { + io.ktor.server.netty.EngineMain.main(args) +} + +fun Application.module() { + configureRouting() +} \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/Routing.kt b/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/Routing.kt new file mode 100644 index 000000000..efc7bf8dc --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/Routing.kt @@ -0,0 +1,35 @@ +package com.example + +import io.ktor.http.ContentType +import io.ktor.server.application.* +import io.ktor.server.http.content.staticResources +import io.ktor.server.plugins.statuspages.StatusPages +import io.ktor.server.response.* +import io.ktor.server.routing.* + +fun Application.configureRouting() { + install(StatusPages) { + exception { call, cause -> + call.respondText("App in illegal state as ${cause.message}") + } + } + routing { + staticResources("/content", "mycontent") + + get("/") { + call.respondText("Hello World!") + } + + // ... + + get("/test1") { + val text = "

Hello From Ktor

" + val type = ContentType.parse("text/html") + call.respondText(text, type) + } + + get("/error-test") { + throw IllegalStateException("Too Busy") + } + } +} diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/com/example/Application.kt b/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/com/example/Application.kt deleted file mode 100644 index 5f5552d9c..000000000 --- a/codeSnippets/snippets/tutorial-server-get-started/src/main/kotlin/com/example/Application.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.example - -import com.example.plugins.* -import io.ktor.server.application.* -import io.ktor.server.engine.* -import io.ktor.server.netty.* - -fun main() { - embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module) - .start(wait = true) -} - -fun Application.module() { - configureRouting() -} diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/application.yaml b/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/application.yaml new file mode 100644 index 000000000..a8bceda96 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/application.yaml @@ -0,0 +1,6 @@ +ktor: + application: + modules: + - com.example.ApplicationKt.module + deployment: + port: 8080 \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/logback.xml b/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/logback.xml index 3e11d7811..aadef5d5b 100644 --- a/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/logback.xml +++ b/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/logback.xml @@ -4,7 +4,7 @@ %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/mycontent/sample.html b/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/mycontent/sample.html index 11aaa1dea..4465d5a0c 100644 --- a/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/mycontent/sample.html +++ b/codeSnippets/snippets/tutorial-server-get-started/src/main/resources/mycontent/sample.html @@ -5,10 +5,10 @@

This page is built with:

-
    -
  1. Ktor
  2. -
  3. Kotlin
  4. -
  5. HTML
  6. -
+
    +
  1. Ktor
  2. +
  3. Kotlin
  4. +
  5. HTML
  6. +
\ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/test/kotlin/ApplicationTest.kt b/codeSnippets/snippets/tutorial-server-get-started/src/test/kotlin/ApplicationTest.kt new file mode 100644 index 000000000..c5e7fe506 --- /dev/null +++ b/codeSnippets/snippets/tutorial-server-get-started/src/test/kotlin/ApplicationTest.kt @@ -0,0 +1,37 @@ +package com.example + +import io.ktor.client.request.get +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType +import io.ktor.server.testing.testApplication +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals + +class ApplicationTest { + + @Test + fun testRoot() = testApplication { + application { + module() + } + val response = client.get("/") + + assertEquals(HttpStatusCode.OK, response.status) + assertEquals("Hello World!", response.bodyAsText()) + } + + @Test + fun testNewEndpoint() = testApplication { + application { + module() + } + + val response = client.get("/test1") + + assertEquals(HttpStatusCode.OK, response.status) + assertEquals("html", response.contentType()?.contentSubtype) + assertContains(response.bodyAsText(), "Hello From Ktor") + } +} \ No newline at end of file diff --git a/codeSnippets/snippets/tutorial-server-get-started/src/test/kotlin/com/example/ApplicationTest.kt b/codeSnippets/snippets/tutorial-server-get-started/src/test/kotlin/com/example/ApplicationTest.kt deleted file mode 100644 index 016bab576..000000000 --- a/codeSnippets/snippets/tutorial-server-get-started/src/test/kotlin/com/example/ApplicationTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.example - -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.testing.* -import org.junit.Assert.assertEquals -import org.junit.Test - -class ApplicationTest { - @Test - fun testRoot() = testApplication { - application { - module() - } - val response = client.get("/") - - assertEquals(HttpStatusCode.OK, response.status) - assertEquals("Hello World!", response.bodyAsText()) - } -} diff --git a/images/ktor_project_generator_new_project_artifact_name.png b/images/ktor_project_generator_new_project_artifact_name.png index 226a50ba0..56c3c0f82 100644 Binary files a/images/ktor_project_generator_new_project_artifact_name.png and b/images/ktor_project_generator_new_project_artifact_name.png differ diff --git a/images/ktor_project_generator_new_project_artifact_name_dark.png b/images/ktor_project_generator_new_project_artifact_name_dark.png index 9d34aa9fc..2a16a7802 100644 Binary files a/images/ktor_project_generator_new_project_artifact_name_dark.png and b/images/ktor_project_generator_new_project_artifact_name_dark.png differ diff --git a/images/ktor_project_generator_new_project_configure.png b/images/ktor_project_generator_new_project_configure.png index 67b820677..253f51901 100644 Binary files a/images/ktor_project_generator_new_project_configure.png and b/images/ktor_project_generator_new_project_configure.png differ diff --git a/images/ktor_project_generator_new_project_configure_dark.png b/images/ktor_project_generator_new_project_configure_dark.png index a2075c07a..afd8e6f71 100644 Binary files a/images/ktor_project_generator_new_project_configure_dark.png and b/images/ktor_project_generator_new_project_configure_dark.png differ diff --git a/images/ktor_project_generator_new_project_download.png b/images/ktor_project_generator_new_project_download.png index 5b7eaacf3..48b832467 100644 Binary files a/images/ktor_project_generator_new_project_download.png and b/images/ktor_project_generator_new_project_download.png differ diff --git a/images/ktor_project_generator_new_project_download_dark.png b/images/ktor_project_generator_new_project_download_dark.png index c0e008bda..c2fad4138 100644 Binary files a/images/ktor_project_generator_new_project_download_dark.png and b/images/ktor_project_generator_new_project_download_dark.png differ diff --git a/images/server_get_started_idea_gradle_run.png b/images/server_get_started_idea_gradle_run.png index a644b3df6..182dbbfe9 100644 Binary files a/images/server_get_started_idea_gradle_run.png and b/images/server_get_started_idea_gradle_run.png differ diff --git a/images/server_get_started_idea_gradle_run_dark.png b/images/server_get_started_idea_gradle_run_dark.png index 880170462..88ad62de6 100644 Binary files a/images/server_get_started_idea_gradle_run_dark.png and b/images/server_get_started_idea_gradle_run_dark.png differ diff --git a/images/server_get_started_idea_main_folder.png b/images/server_get_started_idea_main_folder.png index 70148ccf4..3dd1df842 100644 Binary files a/images/server_get_started_idea_main_folder.png and b/images/server_get_started_idea_main_folder.png differ diff --git a/images/server_get_started_idea_main_folder_dark.png b/images/server_get_started_idea_main_folder_dark.png index 791bc972e..d62da729b 100644 Binary files a/images/server_get_started_idea_main_folder_dark.png and b/images/server_get_started_idea_main_folder_dark.png differ diff --git a/images/server_get_started_idea_project_view.png b/images/server_get_started_idea_project_view.png index 203b7a852..1c75192f6 100644 Binary files a/images/server_get_started_idea_project_view.png and b/images/server_get_started_idea_project_view.png differ diff --git a/images/server_get_started_idea_project_view_dark.png b/images/server_get_started_idea_project_view_dark.png index 3a040fcb0..549d97dd0 100644 Binary files a/images/server_get_started_idea_project_view_dark.png and b/images/server_get_started_idea_project_view_dark.png differ diff --git a/images/server_get_started_idea_resources_folder.png b/images/server_get_started_idea_resources_folder.png index 0d4815e70..f93695254 100644 Binary files a/images/server_get_started_idea_resources_folder.png and b/images/server_get_started_idea_resources_folder.png differ diff --git a/images/server_get_started_idea_resources_folder_dark.png b/images/server_get_started_idea_resources_folder_dark.png index a0ee2aeac..dc148ad6e 100644 Binary files a/images/server_get_started_idea_resources_folder_dark.png and b/images/server_get_started_idea_resources_folder_dark.png differ diff --git a/images/server_get_started_idea_run_terminal.png b/images/server_get_started_idea_run_terminal.png index a9c209767..b8e70b4d3 100644 Binary files a/images/server_get_started_idea_run_terminal.png and b/images/server_get_started_idea_run_terminal.png differ diff --git a/images/server_get_started_idea_run_terminal_dark.png b/images/server_get_started_idea_run_terminal_dark.png index 1d08fb12b..74b84de71 100644 Binary files a/images/server_get_started_idea_run_terminal_dark.png and b/images/server_get_started_idea_run_terminal_dark.png differ diff --git a/topics/lib.topic b/topics/lib.topic index 069eb4add..6ce156039 100644 --- a/topics/lib.topic +++ b/topics/lib.topic @@ -314,12 +314,21 @@ : Choose the desired build system. This can be - Gradle - with Kotlin or Groovy DSL, or - Maven + Gradle Kotlin, + Gradle Groovy, + Maven, or Amper .

+ + +

+ Use version catalog + : + Select this option to use a version catalog (libs.versions.toml) for managing dependency + versions in Gradle and Amper projects. Requires Gradle 7 or later. +

+

Website @@ -360,6 +369,9 @@ to specify server parameters in a YAML or HOCON file, or in code.

+ + YAML configuration is currently not supported for Maven-based Ktor projects. +
diff --git a/topics/maven-assembly-plugin.md b/topics/maven-assembly-plugin.md index c05eb7586..25c9b54db 100644 --- a/topics/maven-assembly-plugin.md +++ b/topics/maven-assembly-plugin.md @@ -21,14 +21,14 @@ To build an assembly, you need to configure the Assembly plugin first: ``` {src="snippets/tutorial-server-get-started-maven/pom.xml" include-lines="10,18-19"} - > If you use [EngineMain](server-create-and-configure.topic#engine-main) without the explicit `main()` function, the application's - > main class depends on the used engine and might look as follows: `io.ktor.server.netty.EngineMain`. - {style="tip"} + In this example `EngineMain` is used to create a server, so the application's main class depends on + the used engine. If you use [embeddedServer](server-create-and-configure.topic#embedded-server), the application's + main class is: `com.example.ApplicationKt`. -2. Add the `maven-assembly-plugin` to the `plugins` block as follows: +2. Add the `maven-assembly-plugin` to the `plugins` block: ```xml ``` - {src="snippets/tutorial-server-get-started-maven/pom.xml" include-lines="111-135"} + {src="snippets/tutorial-server-get-started-maven/pom.xml" include-lines="116-140"} ## Build an assembly {id="build"} diff --git a/topics/server-configuration-file.topic b/topics/server-configuration-file.topic index 490c4712b..a519ea8bf 100644 --- a/topics/server-configuration-file.topic +++ b/topics/server-configuration-file.topic @@ -70,6 +70,9 @@ .

+ + YAML configuration is currently not supported for Maven-based Ktor projects. +

diff --git a/topics/server-create-a-new-project.topic b/topics/server-create-a-new-project.topic index 06aa4efa4..22f247e56 100644 --- a/topics/server-create-a-new-project.topic +++ b/topics/server-create-a-new-project.topic @@ -3,7 +3,7 @@ @@ -21,9 +21,8 @@

In this tutorial, you will learn how to create, open and run - your first Ktor server project. Once you get up and running, you can attempt a series of tasks to familiarize - yourself - with Ktor. + your first Ktor server project. Once you get up and running, you can complete a series of tasks to familiarize + yourself with Ktor.

This is the first of a series of tutorials to get you started with @@ -31,7 +30,7 @@ however, we strongly recommend that you follow the suggested order:

-
  • Create, open and run a new Ktor project.
  • +
  • Create, open, and run a new Ktor project.
  • Handle requests and generate responses.
  • Create a RESTful API that generates JSON.
  • Create a website using Thymeleaf templates.
  • @@ -59,22 +58,22 @@

    -

    Navigate to the Ktor Project Generator.

    +

    Navigate to the Ktor project generator.

    In the Project artifact field, enter - com.example.ktor-sample-app + com.example.ktor-sample as the name of your project artifact. Ktor Project Generator with Project Artifact Name org.example.ktor-sample-app

    - +

    Click Configure to open the settings dropdown menu: @@ -89,6 +88,9 @@

  • +
  • + +
  • @@ -102,7 +104,7 @@
    -

    For this tutorial you can leave the default values for these settings.

    +

    For this tutorial, you can leave the default values for these settings.

    Click @@ -124,7 +126,7 @@ Download button to generate and download your Ktor project. Ktor Project Generator download button @@ -139,11 +141,11 @@ collapsible="true">

    This section describes project setup using the Ktor plugin for Intellij IDEA Ultimate. + href="https://plugins.jetbrains.com/plugin/16008-ktor">Ktor plugin for IntelliJ IDEA Ultimate.

    To create a new Ktor project, - open IntelliJ IDEA, and + open IntelliJ IDEA and follow the steps below:

    @@ -168,18 +170,14 @@
  • - Name - : - Specify a project name. Enter - ktor-sample-app + Name: Specify a project name. Enter + ktor-sample as the name of your project.

  • - Location - : - Specify a directory for your project. + Location: Specify a directory for your project.

  • @@ -287,7 +285,7 @@ Enter - ktor-sample-app + ktor-sample as the name of your project: Using the Ktor CLI tool in interactive mode

    - In this section you will learn how to unpack, build and run the project from the command line. The - descriptions below - assume that: + In this section you will learn how to unpack, build, and run the project from the command line. The + steps below assume that:

    -
  • You have created and downloaded a project called - ktor-sample-app +
  • You have created and downloaded a Gradle project called + ktor-sample .
  • -
  • This has been placed in a folder called +
  • This project is located in a folder called myprojects in your home directory.
  • If necessary, alter the names and paths to match your own setup.

    -

    Open a command line tool of your choice and follow the steps:

    +

    Open a command-line tool of your choice and follow the steps:

    -

    In a terminal, navigate to the folder where you downloaded the project:

    +

    In a terminal window, navigate to the folder where you downloaded the project:

    cd ~/myprojects @@ -362,12 +359,12 @@ - unzip ktor-sample-app.zip -d ktor-sample-app + unzip ktor-sample.zip -d ktor-sample - tar -xf ktor-sample-app.zip + tar -xf ktor-sample.zip @@ -376,12 +373,12 @@

    From the directory, navigate into the newly created folder:

    - cd ktor-sample-app + cd ktor-sample
    -

    On macOS/UNIX systems, you will need to make the gradlew Gradle helper script executable. To do that, - use the chmod command:

    +

    On macOS and UNIX systems, you must make the Gradle helper script executable, so the system + recognizes it as a runnable command. To do that, use the chmod command:

    @@ -404,7 +401,7 @@ -

    If you see that your build has been successful you can execute the project, again via Gradle.

    +

    When the build succeeds, continue to the next step to run the project.

    To run the project, use the following command:

    @@ -422,22 +419,22 @@
    -

    To verify the project is running, open a browser at the URL mentioned in the output (To verify that the project is running, open a browser at the URL shown in the terminal output (http://0.0.0.0:8080). - You should see the message "Hello World!" displayed on the screen:

    - Output of generated ktor project + Output of the generated ktor project

    Congratulations! You have successfully started your Ktor project.

    -

    Note that the command line is unresponsive because the underlying process is busy running the Ktor - application. You can - press + + Note that the command line is unresponsive because the underlying process is busy running the Ktor + application. You can press CTRL+C to terminate the application. -

    + - +

    If you have IntelliJ IDEA installed, you can easily open the project from the command @@ -452,7 +449,7 @@ idea .

    - Alternatively, to open the project manually launch IntelliJ IDEA. + Alternatively, to open the project manually, launch IntelliJ IDEA.

    If the Welcome screen opens, click @@ -461,7 +458,7 @@ File | Open in the main menu and select the - ktor-sample-app + ktor-sample folder to open it.

    @@ -471,36 +468,28 @@
    -

    Whichever option you choose, the project should open as shown below:

    +

    After opening the project, you can see the following structure:

    Generated Ktor project view in IDE

    - In order to explain the project layout we have expanded the structure in the - Project - view and selected the - file - settings-gradle.kts - . + To view the full layout, expand the folders in the Project view by clicking the + expand arrows next to each folder.

    - You will see that the code to run your application lives in packages under + The application source code is located under src/main/kotlin - . The default package is - called - com.example - and contains a subpackage called - plugins - . - Two files have been created within these packages, named + . Two files are created by default, named Application.kt and Routing.kt

    Ktor project src folder structure

    The name of the project is configured in - settings-gradle.kts - . + settings.gradle.kts + with the following contents:

    - Contents of settings.gradle.kt +

    Configuration files, and other kinds of content, live within the src/main/resources @@ -508,12 +497,6 @@

    Ktor project resources folder structure -

    - A skeleton test has been created in a package under - src/test/kotlin - . -

    - Ktor project test folder structure
    @@ -524,11 +507,9 @@ by clicking the Gradle icon (intelliJ IDEA gradle icon) on the right sidebar.

    - Gradle tab in IntelliJ IDEA
    -

    Within this tool window navigate to +

    Within this tool window, navigate to Tasks | application and double-click on the run @@ -538,10 +519,10 @@ border-effect="line" width="450"/> -

    Your Ktor application will start in the Your Ktor application starts in the Run tool window at the bottom of the IDE:

    - Project running in terminal + Project running in the terminal

    The same messages that were previously displayed on the command line will now be visible in the Run tool window. @@ -564,12 +545,12 @@

  • To terminate the application, click the stop button intelliJ IDEA terminate icon + alt="intelliJ IDEA terminate icon"/>.
  • To restart the process, click the rerun button intelliJ IDEA rerun icon + alt="intelliJ IDEA rerun icon"/>.
  • @@ -579,170 +560,157 @@

    - +

    Here are some additional tasks you may wish to try:

    -
  • Change the default port.
  • -
  • Change the port via YAML.
  • -
  • Add a new HTTP endpoint.
  • -
  • Configure static content.
  • -
  • Write an integration test.
  • -
  • Register error handlers.
  • +
  • Change the default port
  • +
  • Add a new HTTP endpoint
  • +
  • Configure static content
  • +
  • Write an integration test
  • +
  • Register error handlers
  • - These tasks do not depend on one another, but gradually increase in complexity. Attempting them in the order - declared is - the easiest way to learn incrementally. For simplicity, and to avoid duplication, the descriptions below - assume you are - attempting the tasks in order. + These tasks do not depend on one another but gradually increase in complexity. Attempting them in the order + declared is the easiest way to learn incrementally. For simplicity and to avoid duplication, the + descriptions below assume you are attempting the tasks in order.

    - Where coding is required we have specified both the code and the corresponding imports. The IDE may add - these imports - for you automatically. + Where coding is required, we have specified both the code and the corresponding imports. The IDE may add + these imports for you automatically.

    -

    - In the - Project - view navigate to the - src/main/kotlin - folder and then into the single package that has been created - for you and follow the steps: -

    - - -

    Open the - Application.kt - file. You should find code similar to the following: -

    - - fun main() { - embeddedServer( - Netty, - port = 8080, // This is the port on which Ktor is listening - host = "0.0.0.0", - module = Application::module - ).start(wait = true) - } - - fun Application.module() { - configureRouting() - } - -
    - -

    In the embeddedServer() function, change the port parameter - to another number of your choosing, such as "9292".

    - - fun main() { - embeddedServer( - Netty, - port = 9292, - host = "0.0.0.0", - module = Application::module - ).start(wait = true) - } - -
    - -

    Click on the rerun button (intelliJ IDEA rerun button icon) - to restart the application.

    -
    - -

    To verify that your application is running under the new port number, you can open your browser - at the new URL (http://0.0.0.0:9292), or - create - a new HTTP Request file in IntelliJ IDEA:

    - Testing port change with an HTTP request file in IntelliJ IDEA -
    -
    -
    - -

    - When creating a new Ktor project, you have the option to store configuration externally, within either a - YAML or - a HOCON file: -

    - Ktor project generator configuration options -

    - If you had chosen to store configuration externally then this would be the code in - Application.kt - : -

    - ): Unit = - io.ktor.server.netty.EngineMain.main(args) + +

    + If you had chosen to store configuration externally, within a YAML or a HOCON file, in the + Project + view navigate to the + src/main/resources + folder and follow the steps: +

    + + + Open your configuration file ( + application.yaml + or + application.conf + ). It should look like the following: + + + + + + + + + + + Change the port value in the file to another number of your choosing, such as + 9292. + + +

    Click on the rerun button (intelliJ IDEA rerun button icon) + to restart the application.

    +
    + +

    To verify that your application is running under the new port number, you can open your browser + at the new URL (http://0.0.0.0:9292) or + create + a new HTTP Request file in IntelliJ IDEA:

    + Testing port change with an HTTP request file in IntelliJ IDEA +
    +
    +
    + +

    + When creating a new Ktor project, you have the option to store + configuration in code or externally, within a YAML or a HOCON file. +

    +

    + If you have chosen the option to store configuration in code, in the + Project + view navigate to the + src/main/kotlin + folder and follow the steps: +

    + + +

    Open the + Application.kt + file. You should find code similar to the following: +

    + + fun main() { + embeddedServer( + Netty, + port = 8080, // This is the port to which Ktor is listening + host = "0.0.0.0", + module = Application::module + ).start(wait = true) + } - @Suppress("unused") - fun Application.module() { - configureRouting() - } - ]]> -

    - These would be the values stored in the configuration file within - src/main/resources/ - : -

    - - - - ktor: - application: - modules: - - com.example.ApplicationKt.module - deployment: - port: 8080 - - - - - ktor { - deployment { - port = 8080 - port = ${?PORT} + fun Application.module() { + configureRouting() } - application { - modules = [ com.example.ApplicationKt.module ] + +
    + +

    In the embeddedServer() function, change the port parameter + to another number of your choosing, such as 9292.

    + + fun main() { + embeddedServer( + Netty, + port = 9292, + host = "0.0.0.0", + module = Application::module + ).start(wait = true) } - } - - - -

    - In this case you do not need to alter any code to change the port number. Simply alter the value in the - YAML or HOCON - file and restart the application. The change can be verified in the same way as - with the default port above. -

    +
    +
    + +

    Click on the rerun button (intelliJ IDEA rerun button icon) + to restart the application.

    +
    + +

    To verify that your application is running under the new port number, you can open your browser + at the new URL (http://0.0.0.0:9292), or + create + a new HTTP Request file in IntelliJ IDEA:

    + Testing port change with an HTTP request file in IntelliJ IDEA +
    + + -

    Next, you will create a new HTTP endpoint that will respond to a GET request.

    In the Project tool window, navigate to the - src/main/kotlin/com/example + src/main/kotlin folder and follow the steps:

    Open the Application.kt - file and find the configureRouting() function. + file and find the .configureRouting() function.

    -

    In IntelliJ IDEA, navigate to the configureRouting() function by placing the caret - over the function name - and pressing - ⌘Cmd+B - . +

    In IntelliJ IDEA, hold Ctrl (Windows/Linux) or + ⌘Cmd (macOS) and click .configureRouting() to navigate to its + definition.

    -

    Alternatively, you can navigate to the function by opening the Routing.kt file.

    +

    Alternatively, you can navigate to the function definition by opening the Routing.kt + file.

    This is the code you should see:

    fun Application.configureRouting() { @@ -755,16 +723,16 @@
    -

    To create a new endpoint, insert the additional five lines of code shown below:

    +

    To create a new endpoint, insert the additional route as shown below:

    -

    Note that you can change the /test1 URL to be whatever you like.

    + src="snippets/tutorial-server-get-started/src/main/kotlin/Routing.kt" + include-lines="10,16,23-29,34-35"/> + Note that you can change the /test1 URL to be whatever you like.
    -

    In order to make use of ContentType, add the following import:

    +

    The IDE automatically adds the import for ContentType:

    - import io.ktor.http.* + import io.ktor.http.ContentType
    @@ -773,18 +741,17 @@ to restart the application.

    -

    Request the new URL in the browser (http://0.0.0.0:9292/test1). The - port number you should use will depend on whether you have attempted the first task (Changing the default port). You should see the - output displayed - below:

    +

    Request the new URL in the browser (http://0.0.0.0:9292/test1). + The port number depends on whether you completed the + Change the default port task. You should see the + output displayed below:

    A browser screen displaying Hello from Ktor -

    If you have created an HTTP Request File you can also verify the new endpoint there:

    +

    If you have created an HTTP request file, you can also verify the new endpoint there:

    An HTTP request file in intelliJ IDEA -

    Note that a line containing three hashes (###) is needed to separate different requests.

    + Note that a line containing three hashes (###) is needed to separate different + requests.
    @@ -792,49 +759,23 @@

    In the Project tool window, navigate to the - src/main/kotlin/com/example/plugins + src/main/kotlin folder and follow the steps:

    -

    Open the Routing.kt file.

    -

    Once again this should be the default content:

    - - fun Application.configureRouting() { - routing { - get("/") { - call.respondText("Hello World!") - } - } - } - -

    For this task it does not matter whether you have inserted the - content for the extra endpoint specified in Adding a new HTTP - endpoint.

    -
    - -

    Add the following line to the routing section:

    - - fun Application.configureRouting() { - routing { - // Add the line below - staticResources("/content", "mycontent") - - get("/") { - call.respondText("Hello World!") - } - } - } - +

    Open the Routing.kt file and add the following route to the routing section:

    +

    The meaning of this line is as follows:

    -
  • Invoking staticResources() tells Ktor that we want our application to be able - to provide standard website content, such - as HTML and JavaScript files. Although this content may be executed within the browser, it - is considered static from - the server's point of view. +
  • Invoking staticResources() enables your application + to provide standard website content, such as HTML and JavaScript files. Although this + content can be executed within the browser, it is considered static from the server's point + of view.
  • -
  • The URL /content specifies the path that should be used to fetch this content. +
  • The URL /content specifies the path used to fetch this content.
  • The path mycontent is the name of the folder within which the static content will live. Ktor will look for this @@ -843,21 +784,21 @@ -

    Add the following import:

    +

    Add the following import if the IDE doesn’t add it automatically.

    - import io.ktor.server.http.content.* + import io.ktor.server.http.content.staticResources

    In the Project - tool window, right-click the src/main/resources folder and select + tool window, right-click the src/main/resources folder and select New | Directory .

    -

    Alternatively, select the src/main/resources folder, press - ⌘Сmd+N - , and click +

    Alternatively, select the src/main/resources folder, press + ⌘Cmd+N (macOS) or Ctrl+N (Windows/Linux) + and click Directory .

    @@ -875,7 +816,7 @@

    -

    Name the new file "sample.html" and press +

    Name the new file sample.html and press ↩Enter .

    @@ -884,7 +825,7 @@

    Populate the newly created file page with valid HTML, for example:

    + />

    Click on the rerun button (intelliJ IDEA rerun button icon

    - Create a new directory under + Create a new subdirectory under src - called "test" and a subdirectory called "kotlin". -

    -
    - -

    Create a new package inside - src/test/kotlin - called "com.example". + called test/kotlin.

    Within - src/test/kotlin/com.example - create a new file called "ApplicationTest.kt". + src/test/kotlin + create a new ApplicationTest.kt file.

    -

    Open the ApplicationTest.kt file and add the code below:

    - - class ApplicationTest { - - @Test - fun testRoot() = testApplication { - application { - module() - } - val response = client.get("/") - - assertEquals(HttpStatusCode.OK, response.status) - assertEquals("Hello World!", response.bodyAsText()) - } - } - +

    Open the ApplicationTest.kt file and add the code below:

    +

    The testApplication() method creates a new instance of Ktor. This instance is running inside a test environment, as opposed to a server such as Netty.

    @@ -955,61 +879,50 @@

    Add the following required imports:

    - import io.ktor.client.request.* - import io.ktor.client.statement.* - import io.ktor.http.* - import io.ktor.server.testing.* - import org.junit.Assert.assertEquals - import org.junit.Test + import io.ktor.client.request.get + import io.ktor.client.statement.bodyAsText + import io.ktor.http.HttpStatusCode + import io.ktor.server.testing.testApplication + import kotlin.test.Test + import kotlin.test.assertEquals

    - The test can be run in any of the standard ways for executing tests in IntelliJ IDEA. Note that, because + You can run the test in any of the standard ways for executing tests in IntelliJ IDEA. Note that, because you are running a new instance of Ktor, the success or failure of the test does not depend on whether your application is running at - 0.0.0.0. + 0.0.0.0

    If you have successfully completed adding a new HTTP endpoint, - you should be able to add this additional test:

    + +

    Add the following additional imports:

    - @Test - fun testNewEndpoint() = testApplication { - application { - module() - } - - val response = client.get("/test1") - - assertEquals(HttpStatusCode.OK, response.status) - assertEquals("html", response.contentType()?.contentSubtype) - assertContains(response.bodyAsText(), "Hello From Ktor") - } - -

    The following additional import is required:

    - + import io.ktor.http.contentType import kotlin.test.assertContains

    - You can handle errors in your Ktor application by using the StatusPages + You can handle errors in your Ktor application using the StatusPages plugin.

    -

    - This plugin is not included in your project by default. You could have added it to your project via the + + This plugin is not included in your project by default. You can add it to your project via the Plugins - section in the Ktor - Project Generator, or the Project Wizard in IntelliJ IDEA. Since you've already created your project, in - the next steps - you will learn how to add and configure the plugin manually. -

    + section while creating a project with the Ktor project generator, or the Project Wizard in + IntelliJ IDEA. +

    - There are four steps to achieving this: + In the next steps you will learn how to add and configure the plugin manually. There are four steps to + achieving this:

  • Add a new dependency in the Gradle build file.
  • @@ -1024,48 +937,42 @@ tool window, navigate to the project root folder and follow the steps:

    -

    Open the build.gradle.kts file.

    +

    Open the gradle/libs.versions.toml file and add the following new library:

    + + [libraries] + #... + ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" } +
    -

    In the dependencies section add the extra dependency as shown below:

    - - dependencies { - // The new dependency to be added - implementation("io.ktor:ktor-server-status-pages:$ktor_version") - // The existing dependencies - implementation("io.ktor:ktor-server-core-jvm:$ktor_version") - implementation("io.ktor:ktor-server-netty-jvm:$ktor_version") - implementation("ch.qos.logback:logback-classic:$logback_version") - testImplementation("io.ktor:ktor-server-test-host-jvm:$ktor_version") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version") - } - -

    When you have done this you will need to reload the project to pick up this new dependency.

    +

    Open the build.gradle.kts file and add the new dependency as shown below:

    +

    Reload the project by pressing - Shift+⌘Cmd+I - on macOS or - Ctrl+Shift+O - on Windows. + Shift+⌘Cmd+I (macOS) or + Ctrl+Shift+O (Windows/Linux).

    -

    Navigate to the configureRouting() method in Routing.kt and add the +

    Navigate to the .configureRouting() method in Routing.kt and add the following lines of code:

    + src="snippets/tutorial-server-get-started/src/main/kotlin/Routing.kt" + include-lines="10-16,23,34-35"/>

    These lines install the StatusPages plugin and specify what actions to take when an exception of type IllegalStateException is thrown.

    Add the following import:

    - import io.ktor.server.plugins.statuspages.* + import io.ktor.server.plugins.statuspages.StatusPages
    @@ -1075,14 +982,13 @@

    -

    Staying within the configureRouting() method, add the additional lines as shown +

    Staying within the configureRouting() method, add the additional route as shown below:

    + src="snippets/tutorial-server-get-started/src/main/kotlin/Routing.kt" + include-lines="10-16,23,30-35"/>

    You have now added an endpoint with the URL /error-test. When this endpoint is - triggered, an - exception will be thrown with the type used in the handler.

    + triggered, an exception is thrown with the type used in the handler.