Auto reload

note

Auto reload - 图1

This help topic is in development and will be updated in the future.

During development, it is important to have a fast feedback loop cycle. Often, restarting the server can take some time which is why Ktor provides a basic auto-reload facility that reloads our Application classes.

note

Auto reload - 图2

Autoreload is experimental under JDK9+. If it doesn’t work, feel free to open an issue and please stick to JDK 8 in this case. There is a performance penalty when using auto-reloading. So keep in mind that you should not use it in production or when doing benchmarks.

Automatic reloading on class changes

In both cases, when using the embeddedServer or a configuration file, you will have to provide a list of watch substrings that should match the classloaders you want to watch.

So for example, a typical class loader when using gradle would look like: \ /Users/user/projects/ktor-exercises/solutions/exercise4/build/classes/kotlin/main

In this case, you can use the solutions/exercise4 string or just exercise4 when watching, so it will match that classloader.

Using embeddedServer

When using a custom main and embeddedServer, you can use the optional parameter watchPaths to provide a list of sub-paths that will be watched and reloaded.

  1. fun main(args: Array<String>) {
  2. embeddedServer(
  3. Netty,
  4. watchPaths = listOf("solutions/exercise4"),
  5. port = 8080,
  6. module = Application::mymodule
  7. ).apply { start(wait = true) }
  8. }
  9. fun Application.mymodule() {
  10. routing {
  11. get("/plain") {
  12. call.respondText("Hello World!")
  13. }
  14. }
  15. }

note

Auto reload - 图3

When using watchPaths you should not use a lambda to configure the server, but to provide a method reference to your Application module.

If you try to use a lambda instead of a method reference, you will get the following error:

  1. Exception in thread "main" java.lang.RuntimeException: Module function provided as lambda cannot be unlinked for reload

To fix this error, you just have to extract your lambda body to an Application extension method (module) just like this:

❌ Code that won’t work:

  1. fun main(args: Array<String>) {
  2. // ERROR! Module function provided as lambda cannot be unlinked for reload
  3. embeddedServer(Netty, watchPaths = listOf("solutions/exercise4"), port = 8080) {
  4. routing {
  5. get("/") {
  6. call.respondText("Hello World!")
  7. }
  8. }
  9. }.start(true)
  10. }

✅ Code that will work:

  1. fun main(args: Array<String>) {
  2. embeddedServer(
  3. Netty, watchPaths = listOf("solutions/exercise4"), port = 8080,
  4. // GOOD!, it will work
  5. module = Application::mymodule
  6. ).start(true)
  7. }
  8. // Body extracted to a function acting as a module
  9. fun Application.mymodule() {
  10. routing {
  11. get("/") {
  12. call.respondText("Hello World!")
  13. }
  14. }
  15. }

Using the application.conf

When using a configuration file, for example with an EngineMain to either run from the command line or hosted within a server container:

To enable this feature, add watch keys to ktor.deployment configuration.

watch - Array of classpath entries that should be watched and automatically reloaded.

  1. ktor {
  2. deployment {
  3. port = 8080
  4. watch = [ module1, module2 ]
  5. }
  6. }

For now watch keys are just strings that are matched with contains, against the classpath entries in the loaded application, such as a jar name or a project directory name. These classes are then loaded with a special ClassLoader that is recycled when a change is detected.

note

Auto reload - 图4

ktor-server-core classes are specifically excluded from auto-reloading, so if you are working on something in ktor itself, don’t expect it to be reloaded automatically. It cannot work because core classes are loaded before the auto-reload kicks in. The exclusion can potentially be smaller, but it is hard to analyze all the transitive closure of types loaded during startup.

note

Auto reload - 图5

Classpath entries look like file:///path/to/project/build/classes/myproject.jar, so to/project would match, but com.mydomain would not.

Recompiling automatically on source changes

Since the Autoreload feature only detects changes in class files, you have to compile the application by yourself. You can do it using IntelliJ IDEA with Build -> Build Project while running.

However, you can also use gradle to automatically detect source changes and compile it for you. You can just open another terminal in your project folder and run: gradle -t installDist.

It will compile the application, and after doing so, it will listen for additional source changes and recompile when necessary. And thus, triggering Automatic class reloading.

You can then use another terminal to run the application with gradle run. If you use IntelliJ IDEA to run the application, you should properly configure its compilation output locations because it uses a different output location from that gradle uses.

Example

Consider the following example:

You can run the application by using either a build.gradle or directly within your IDE. Executing the main method in the example file, or by executing: io.ktor.server.netty.EngineMain.main. EngineMain using commandLineEnvironment will be in charge of loading the application.conf file (that is in HOCON format).

Auto reload - 图6

Kotlin

Kotlin

Auto reload - 图7

  1. package io.ktor.exercise.autoreload
  2. import io.ktor.application.*
  3. import io.ktor.http.*
  4. import io.ktor.response.*
  5. import io.ktor.routing.*
  6. import io.ktor.server.engine.*
  7. import io.ktor.server.netty.*
  8. // Exposed as: `static void io.ktor.exercise.autoreload.MainKt.main(String[] args)`
  9. fun main(args: Array<String>) {
  10. //io.ktor.server.netty.main(args) // Manually using Netty's EngineMain
  11. embeddedServer(
  12. Netty, watchPaths = listOf("solutions/exercise4"), port = 8080,
  13. module = Application::module
  14. ).apply { start(wait = true)
  15. }
  16. // Exposed as: `static void io.ktor.exercise.autoreload.MainKt.module(Application receiver)`
  17. fun Application.module() {
  18. routing {
  19. get("/plain") {
  20. call.respondText("Hello World!")
  21. }
  22. }
  23. }

As you can see, you need to specify a list of strings to match the classloaders you want to watch –in this case only solutions/exercise4– which should then be reloaded upon modification.