I have written about my thoughts on hot loading in general in this post.
Micronaut from version 1.2 started supporting continuous build, but you had to manually add the configuration entries to your project. With the 2.0 version, Micronaut supports 'continuous build' out of the box.
Both Gradle and Micronaut contribute to reload. If you wish to understand how Gradle's continuous build works on a standalone Java project, take a look at this post.
Let's launch the Micronaut CLI using mn
command and create a Micronaut application as follows.
mn> create-app com.nareshak.demo.helloservice
Let's cd
into helloservice
directory and launch mn
command. Now let's generate controller as follows.
mn> create-controller com.nareshak.demo.Hello
| Rendered controller to src/main/java/com/nareshak/demo/HelloController.java
| Rendered test to src/test/java/com/nareshak/demo/HelloControllerTest.java
Let's take a look at the generated controller HelloController.java
.
package com.nareshak.demo;
import io.micronaut.http.annotation.*;
@Controller("/hello")
public class HelloController {
@Get(uri="/", produces="text/plain")
public String index() {
return "Example Response";
}
}
Let's run the application from the command-line making use of Gradle's continuous build feature as follows.
➜ helloservice ./gradlew run -t
> Task :run
17:05:41.147 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 718ms. Server Running: http://localhost:8080
You could also use --continuous
instead of -t
. Let's launch another terminal window and invoke the endpoint "/hello".
➜ ~ curl -i http://localhost:8080/hello
HTTP/1.1 200 OK
Date: Sat, 1 Aug 2020 11:37:03 GMT
content-type: text/plain
content-length: 16
connection: keep-alive
Example Response
Now, that the application is working as expected, Let's modify the message from "Example Response" to "Hello". Let's take a look at the terminal window where the application is running.
17:07:59.449 [micronaut-filewatch-thread] INFO i.m.r.s.w.e.FileWatchRestartListener - Shutting down server following file change.
17:07:59.450 [Thread-1] INFO io.micronaut.runtime.Micronaut - Embedded Application shutting down
BUILD SUCCESSFUL in 2m 20s
3 actionable tasks: 1 executed, 2 up-to-date
Waiting for changes to input files of tasks... (ctrl-d to exit)
modified: /Users/naresha/demo/helloservice/src/main/java/com/nareshak/demo/HelloController.java
Change detected, executing build...
> Task :compileJava
Note: Creating bean classes for 1 type elements
> Task :run
17:08:02.334 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 966ms. Server Running: http://localhost:8080
Notice how our change to file "HelloController.java" is detected and Gradle brought up the server with our code changes. Let's validate if the change we made came into effect.
➜ ~ curl -i http://localhost:8080/hello
HTTP/1.1 200 OK
Date: Sat, 1 Aug 2020 11:38:41 GMT
content-type: text/plain
content-length: 5
connection: keep-alive
Hello
Great. We are now getting the updated message "Hello".
Both Gradle and Micronaut have their mechanisms to watch for the changes to the files of interest. Upon noticing a file change, Micronaut shuts down the server (Note the log entry from FileWatchRestartListener
above. The next log entry is from the shutdown hook). Once the java process terminates, Gradle re-executes the run
task, and another instance of the java process is created to run the Micronaut application.
Now let's take a look at the configurations present in build.gradle
which made reloading possible. I have shown only those configurations that are relevant for restart. Also, note that I am using Mac OS, which requires a few additional settings.
build.gradle
configurations {
// for dependencies that are needed for development only
developmentOnly
}
dependencies {
developmentOnly("io.micronaut:micronaut-runtime-osx:$micronautVersion")
}
test.classpath += configurations.developmentOnly
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.compilerArgs.addAll([
'-parameters',
// enables incremental compilation
'-Amicronaut.processing.incremental=true',
'-Amicronaut.processing.annotations=com.nareshak.demo.*',
"-Amicronaut.processing.group=$project.group",
"-Amicronaut.processing.module=$project.name",
])
}
tasks.withType(JavaExec) {
classpath += configurations.developmentOnly
jvmArgs('-XX:TieredStopAtLevel=1', '-Dcom.sun.management.jmxremote')
if (gradle.startParameter.continuous) {
systemProperties(
'micronaut.io.watch.restart':'true',
'micronaut.io.watch.enabled':'true',
"micronaut.io.watch.paths":"src/main"
)
}
}
For OS X, a development only dependency is added, which enables watching for file changes. OS X file watching functionality makes use of https://github.com/gmethvin/directory-watcher. Then there are configurations for enabling annotation processor in incremental mode. Finally, if we run Gradle in continuous build mode, Micronaut will enable shutting down the server upon a file change.
I have used Micronaut 2.0.1 for the code examples.