plugins {
id 'io.micronaut.application' version '4.4.4'
}
Table of Contents
- 1. Compatibility notes
- 2. The Gradle plugins
- 3. Quick Start
- 4. Selecting the Micronaut version
- 5. Micronaut Library Plugin
- 6. Micronaut Application Plugin
- 7. Micronaut GraalVM Plugin
- 8. Micronaut AOT Plugin
- 9. Automatic test resources provisioning
- 10. Micronaut CRaC Plugin
- 11. OpenAPI code generation
- 12. Additional Notes
- 13. Upgrade notes
Micronaut Gradle plugin
Version 4.4.4
1. Compatibility notes
This Gradle plugin collection is designed for Micronaut 4.x and requires Gradle 8+. This version defaults to Micronaut 4.6.3 and is tested with Gradle 8.10.2.
Micronaut 3.x users should use the 3.x branch which documentation can be found here.
Micronaut 2.x users, should use the 2.x branch which documentation can be found here.
2. The Gradle plugins
A Gradle Plugin which makes development of Micronaut application and libraries a breeze.
This project currently consists of different plugins:
-
the
io.micronaut.application
plugin is aimed at building Micronaut applications. It automatically integrates with GraalVM by applying theio.micronaut.graalvm
plugin transparently. -
the
io.micronaut.library
plugin is aimed at building Micronaut libraries. -
the
io.micronaut.graalvm
plugin integrates with the official GraalVM Native Build Tools plugin and configures it for Micronaut applications. -
the
io.micronaut.aot
plugin integrates with Micronaut AOT to produce optimized binaries. -
the
io.micronaut.platform.catalog
plugin imports the Micronaut Platform version catalog so that you can easily add Micronaut dependencies in your build scripts.
2.1. Individual plugins
In addition to the library and application plugins described in the following documentation, you can apply one of the following plugins to your project.
For example, the minimal
plugins can be used to reduce the number of tasks, for example if you don’t need Docker or GraalVM support:
Plugin id | Description | Also applies |
---|---|---|
Allows building Micronaut libraries, without GraalVM support |
|
|
Allows building Micronaut applications, without GraalVM nor Docker support |
|
|
Adds support for building native executables |
||
Adds support for building Docker images |
||
Adds support for Micronaut AOT |
||
A typical Micronaut Library, with support for GraalVM |
|
|
A typical Micronaut Application, with support for GraalVM and Docker |
|
|
Provides automatic test resources provisioning on a single project |
||
Provides ability to use test resources of other projects of multiproject builds |
||
Adds support for generating OpenAPI clients or servers from an OpenAPI definition. |
Using the io.micronaut.application
plugin:
plugins {
id("io.micronaut.application") version "4.4.4"
}
is therefore equivalent to applying those plugins individually:
plugins {
id 'io.micronaut.minimal.application' version '4.4.4'
id 'io.micronaut.docker' version '4.4.4'
id 'io.micronaut.graalvm' version '4.4.4'
}
apply plugin: com.diffplug.gradle.eclipse.apt.AptEclipsePlugin
plugins {
id("io.micronaut.application") version "4.4.4"
id("io.micronaut.docker") version "4.4.4"
id("io.micronaut.graalvm") version "4.4.4"
}
apply(plugin=com.diffplug.gradle.eclipse.apt.AptEclipsePlugin::class.java)
io.micronaut.minimal.application
, io.micronaut.graalvm
and io.micronaut.docker
plugins (as well as the Eclipse annotation processing support plugin).
3. Quick Start
Template projects are available via Micronaut Launch for each language.
To get started you can use the Micronaut CLI:
$ mn create-app demo --lang java
$ mn create-app demo --lang groovy
$ mn create-app demo --lang kotlin
Or if you don’t have it installed via curl
:
# for Java
$ curl https://launch.micronaut.io/demo.zip?lang=java \
-o demo.zip && unzip demo.zip -d demo && cd demo
# for Groovy
$ curl https://launch.micronaut.io/demo.zip?lang=groovy \
-o demo.zip && unzip demo.zip -d demo && cd demo
# for Kotlin
$ curl https://launch.micronaut.io/demo.zip?lang=kotlin \
-o demo.zip && unzip demo.zip -d demo && cd demo
4. Selecting the Micronaut version
4.1. The Micronaut Platform Catalog plugin
We recommend that you apply the io.micronaut.platform.catalog
plugin to your settings.gradle(.kts)
file.
This plugin will automatically import the Micronaut version catalog, which provides a number of advantages:
-
all supported Micronaut modules are part of the platform catalog, making it easy to add new dependencies on Micronaut modules without having to remember their group, artifact and version numbers
-
all transitive dependencies of Micronaut modules are shipped with a recommended version that you can find in the version catalog
-
you can easily override versions of individual Micronaut modules or even transitive dependencies which are managed by the Micronaut platform
Important
|
This is a settings plugin, which means that it must be applied to your settings.gradle(.kts) file, not build.gradle(.kts) .
|
Add the plugin to your settings.gradle(.kts)
file:
plugins {
id 'io.micronaut.platform.catalog' version '4.4.4'
}
plugins {
id("io.micronaut.platform.catalog") version "4.4.4"
}
The catalog is then exposed with the name mn
and can be used in your build scripts, for example:
dependencies {
// Use Spring Boot annotations
compileOnly mn.micronaut.spring.boot.annotation
}
dependencies {
// Use Spring Boot annotations
compileOnly(mn.micronaut.spring.boot.annotation)
}
4.2. Selecting the Micronaut Platform version
The minimum requirement is to set the Micronaut version to use. Since Micronaut 4, this is the version of the Micronaut Platform (in previous releases, releases were bound to the version of Micronaut Core).
The easiest is to set micronautVersion
in gradle.properties
.
If you use a version catalog, you can also set the version of Micronaut directly in your libs.versions.toml
file:
[versions]
micronaut="4.0.0"
This version will be shared by all Micronaut modules of your project.
Alternatively, you can set the version in your build.gradle(.kts)
:
micronaut {
version "4.6.3"
}
micronaut {
version.set("4.6.3")
}
4.3. Overriding Micronaut module versions
Micronaut offers multiple options to override the default versions of its modules.
For example, you may want to try a SNAPSHOT
version of a module, or a release version which is not yet included in a new platform release.
The easiest to do this is to make use of the Micronaut Platform version catalog.
4.3.1. Using the Micronaut Platform Catalog plugin
If you use the Micronaut Platform Catalog plugin, then overriding versions of individual modules can easily be done by creating a gradle/mn-override.versions.toml
file:
[versions]
micronaut-core = "4.5.0" # override version of Micronaut Core
ehcache = "3.8.2" # override version of ehcache
Note that overriding will only work if you use the dependency notations from the platform catalog (e.g implementation(mn.ehcache)
).
4.3.2. Using the DSL
Alternatively, in each project which applies the Micronaut plugins, you can override versions of specific modules via the DSL:
micronaut {
coreVersion.set("4.5.0") // override the version of Micronaut Core
}
micronaut {
coreVersion.set("4.5.0") // override the version of Micronaut Core
}
There is only a limited list of module versions which can be overridden via the DSL:
-
coreVersion
for Micronaut Core -
awsVersion
for Micronaut AWS -
azureVersion
for Micronaut Azure -
dataVersion
for Micronaut Data -
gcpVersion
for Micronaut Google Cloud -
jaxrsVersion
for Micronaut JAX-RS -
oraclecloudVersion
for Micronaut Oracle Cloud -
securityVersion
for Micronaut Security -
servletVersion
for Micronaut Servlet -
validationVersion
for Micronaut Validation
5. Micronaut Library Plugin
plugins {
id 'io.micronaut.library' version '4.4.4'
}
plugins {
id("io.micronaut.library") version "4.4.4"
}
The Micronaut library plugin applies the following modifications to the build:
-
Applies the Micronaut Bill of Materials (BOM)
-
Applies the
java-library
plugin -
Configures annotation processing for the current language (Groovy, Java or Kotlin)
-
Adds annotation processors automatically for Micronaut modules
The micronaut
DSL can be used to configure how this behaves.
Complete example with the default settings:
micronaut {
version "4.6.3"
// If set to false, then the `io.micronaut.platform:micronaut-platform` BOM
// will not be automatically applied, and you will have to add it yourself
// to your dependencies, or specify versions of Micronaut modules explicitly
importMicronautPlatform = true
processing {
// Sets whether incremental annotation processing is enabled
incremental true
// Sets the module name.
// This should be the same as the artifactId in the POM
module project.name
// Sets the group.
// This should be th same as the groupId in the POM
group project.group
// Sets the Java package names containing any custom Micronaut
// meta annotations (new annotations annotated with say @Around).
// Generally used only for advanced cases such as defining new AOP
// advice. If omitted however, incremental annotation processing
// will not work correctly
annotations "com.example.*"
// additional sourceSets can be configured here to apply the BOM
// and annotation processors to source sets other than 'main'
sourceSets(
sourceSets.main
)
}
}
micronaut {
version.set("4.6.3")
// If set to false, then the `io.micronaut.platform:micronaut-platform` BOM
// will not be automatically applied, and you will have to add it yourself
// to your dependencies, or specify versions of Micronaut modules explicitly
importMicronautPlatform.set(true)
processing {
// Sets whether incremental annotation processing is enabled
incremental.set(true)
// Sets the module name.
// This should be the same as the artifactId in the POM
module.set(project.name)
// Sets the group.
// This should be th same as the groupId in the POM
group.set(project.group)
// Sets the Java package names containing any custom Micronaut
// meta annotations (new annotations annotated with say @Around).
// Generally used only for advanced cases such as defining new AOP
// advice. If omitted however, incremental annotation processing
// will not work correctly
annotations.add("com.example.*")
// additional sourceSets can be configured here to apply the BOM
// and annotation processors to source sets other than 'main'
sourceSets(
sourceSets.findByName("main")
)
}
}
Note
|
The Micronaut Library plugin also supports Groovy and Kotlin sources. |
5.1. Kotlin Support
For Kotlin, the Kotlin jvm
and kapt
plugins must be configured:
plugins {
id "org.jetbrains.kotlin.jvm" version "1.9.25"
id "org.jetbrains.kotlin.kapt" version "1.9.25"
id "io.micronaut.library" version "4.4.4"
}
plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.25"
id("org.jetbrains.kotlin.kapt") version "1.9.25"
id("io.micronaut.library") version "4.4.4"
}
5.2. Minimal Build
With the io.micronaut.library
plugin applied a minimal build to get started writing a library for Micronaut that written in Java and is tested with JUnit 5 looks like:
plugins {
id 'io.micronaut.library' version '4.4.4'
}
version "0.1"
group "com.example"
repositories {
mavenCentral()
}
micronaut {
version = "4.6.3"
}
dependencies {
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
plugins {
id("io.micronaut.library") version "4.4.4"
}
version = "0.1"
group = "com.example"
repositories {
mavenCentral()
}
micronaut {
version.set("4.6.3")
}
dependencies {
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
6. Micronaut Application Plugin
plugins {
id "io.micronaut.application" version "4.4.4"
}
plugins {
id("io.micronaut.application") version "4.4.4"
}
The Micronaut application plugin extends the Micronaut Library plugin and adds the following customizations:
-
Instead of the
java-library
plugin the plugin applies the Gradleapplication
plugin -
Applies the
io.micronaut.graalvm
plugin -
Correctly configures Gradle for continuous build
The following additional tasks are provided by this plugin:
-
buildLayers
- Builds application layers for use in a Docker container -
dockerfile
- Builds a Docker File for a Micronaut application -
dockerBuild
- Builds a Docker Image using the Docker Gradle plugin -
dockerfileNative
- Builds a Docker File for GraalVM Native Image -
dockerBuildNative
- Builds a Native Docker Image using GraalVM Native Image -
dockerBuildCrac
- Builds a docker Image containing a CRaC enabled JDK and a pre-warmed, checkpointed application. -
dockerFileCrac
- Builds a Docker File for a CRaC checkpointed image. -
nativeCompile
- Builds a GraalVM Native Executable -
dockerPush
- Pushes a Docker Image to configured container registry -
dockerPushNative
- Pushes a Docker Image built with GraalVM Native Image to configured container registry -
dockerPushCrac
- Pushes a Docker Image built with a CRaC enabled JDK and a pre-warmed, checkpointed application to configured container registry
To run an application with continuous build use the run
task with the -t
parameter:
$ ./gradlew run -t
6.1. Minimal Build
With the io.micronaut.application
plugin applied a minimal build to get started with a Micronaut server application that is written in Java and tested with JUnit 5 looks like:
plugins {
id 'io.micronaut.application' version '4.4.4'
}
version "0.1"
group "com.example"
repositories {
mavenCentral()
}
micronaut {
version = "4.6.3"
}
dependencies {
implementation("io.micronaut:micronaut-http-server-netty")
runtimeOnly("ch.qos.logback:logback-classic")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
application {
mainClass = "example.Application"
}
plugins {
id("io.micronaut.application") version "4.4.4"
}
version = "0.1"
group = "com.example"
repositories {
mavenCentral()
}
micronaut {
version.set("4.6.3")
}
dependencies {
implementation("io.micronaut:micronaut-http-server-netty")
runtimeOnly("ch.qos.logback:logback-classic")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
application {
mainClass.set("example.Application")
}
6.2. Kotlin Support
The most simple Kotlin build using a build.gradle(.kts)
file looks like:
plugins {
id "org.jetbrains.kotlin.jvm" version "1.9.25"
id "org.jetbrains.kotlin.kapt" version "1.9.25"
id "org.jetbrains.kotlin.plugin.allopen" version "1.9.25"
id "io.micronaut.application" version "4.4.4"
}
version "0.1"
group "com.example"
repositories {
mavenCentral()
}
micronaut {
version = "4.6.3"
}
dependencies {
implementation("io.micronaut:micronaut-http-server-netty")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.25")
runtimeOnly("ch.qos.logback:logback-classic")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
application {
mainClass = "example.ApplicationKt"
}
plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.25"
id("org.jetbrains.kotlin.kapt") version "1.9.25"
id("org.jetbrains.kotlin.plugin.allopen") version "1.9.25"
id("io.micronaut.application") version "4.4.4"
}
version = "0.1"
group = "com.example"
repositories {
mavenCentral()
}
micronaut {
version.set("4.6.3")
}
dependencies {
implementation("io.micronaut:micronaut-http-server-netty")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.25")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.25")
runtimeOnly("ch.qos.logback:logback-classic")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
application {
mainClass.set("example.ApplicationKt")
}
6.3. GraalVM Native Image
Since version 3.0.0, the Micronaut plugins rely on the official GraalVM plugin to build native executables.
Those plugins make use of Gradle toolchains support, which means that the SDK which is used to build the native is decorrelated from the JVM which is used to launch Gradle itself. Said differently, you can run Gradle with OpenJDK, while still building native executables using the GraalVM SDK.
The Micronaut Gradle plugin will automatically configure the toolchains support for you, but there are a few things that you should be aware of:
-
running Gradle with a GraalVM SDK doesn’t necessarily imply that Gradle will use the same SDK to build native executables
-
Gradle will try to locate a compatible GraalVM toolchain to build executables. You can tweak what GraalVM version to use by following the official documentation.
Important
|
While the toolchain selection will properly select a GraalVM SDK which matches your language version requirements, it will not let you pick a particular GraalVM version (say, prefer 21.3 over 21.1). If your application depends on a specific GraalVM version, you will have to disable automatic detection like explained below. |
If you have several GraalVM installations available, or that you want to disable the automatic toolchain recognition, we recommend that you do the following:
-
set up an environment variable named
GRAALVM_HOME
pointing to your GraalVM installation -
edit your
gradle.properties
file to add the following options:
# Disable Gradle automatic download of Java SDKs
org.gradle.java.installations.auto-download=false
# Disable auto-detection of Java installations
org.gradle.java.installations.auto-detect=false
# Setup explicitly that the Java version to use
# should be the one from the JAVA_HOME environment variable
org.gradle.java.installations.fromEnv=JAVA_HOME
Alternatively you can pass those options from the command line:
./gradlew -Porg.gradle.java.installations.auto-download=false \
-Porg.gradle.java.installations.auto-detect=false \
-Porg.gradle.java.installations.fromEnv=JAVA_HOME \
build
You can build a native executable by running the following task:
$ ./gradlew nativeCompile
And you can run it by calling the following task:
$ ./gradlew nativeRun
You can tweak the native executable options by configuring the graalvmNative
extension as explained in the plugin documentation.
For example, you can add options to the main executable by doing:
graalvmNative {
binaries {
main {
buildArgs.add("-H:-DeleteLocalSymbols")
buildArgs.add("-H:+PreserveFramePointer")
}
}
}
graalvmNative {
binaries {
named("main") {
buildArgs.add("-H:-DeleteLocalSymbols")
buildArgs.add("-H:+PreserveFramePointer")
}
}
}
Important
|
If you update an existing Micronaut application that contains the file src/main/resources/META-INF/native-image/xxxxx/native-image.properties , please make sure to delete the properties -H:Name and -H:Class from the file because they are managed automatically by the plugin.
|
6.3.1. Build "mostly static" native executables
Since GraalVM 21.0, it is possible to create "mostly static" native images that can run in a distroless docker image.You only need to configure the appropriate baseImage and the plugin will automatically configure GraalVM:
tasks.named('dockerfileNative') {
baseImage('gcr.io/distroless/cc-debian10')
}
tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
baseImage("gcr.io/distroless/cc-debian10")
}
In case you want to use another base image you need to set the appropriate GraalVM flag:
tasks.named('dockerfileNative') {
baseImage(...)
args('-H:+StaticExecutableWithDynamicLibC')
}
tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
baseImage(...)
args("-H:+StaticExecutableWithDynamicLibC")
}
6.4. Docker Support
6.4.1. Building docker images
The Micronaut plugin includes integration with the Gradle Docker plugin allowing you to easily build applications and native executables using Docker containers.
Applications are built as layered JARs using the buildLayers
task ensuring optimized Docker images for Java applications.
To build a regular Java application into a Docker container that is ready to be deployed and exposes ports 8080
you can simply do:
$ ./gradlew dockerBuild
The default uses an eclipse-temurin:17-jre
base image, however you can easily switch the base image to use with the baseImage
property of the dockerfile
task:
tasks.named("dockerfile") {
baseImage = "oracle/graalvm-ce:22.3.2-java17"
}
tasks.named<MicronautDockerfile>("dockerfile") {
baseImage.set("oracle/graalvm-ce:22.3.2-java17")
}
The above examples switches to use GraalVM CE 22.3.2 as a base image.
To build the application into a Native Executable you can run:
$ ./gradlew dockerBuildNative
Note that for this to work you must build the application with the same GraalVM SDK as used to build the executable.
To build a docker image containing a CRaC enabled JDK and a pre-warmed, checkpointed application, you can run:
$ ./gradlew dockerBuildCrac
To push the container to the currently configured container registry you can use either dockerPush
, dockerPushNative
for the native executable, or dockerPushCrac
for CRaC image:
$ ./gradlew dockerPush
To configure the image names to push you can use the images
setting of the dockerBuild
task.
For example the following configures dockerPush
to use Oracle Container Registry:
tasks.named("dockerBuild") {
images = ["eu-frankfurt-1.ocir.io/xyzzyz/repo/my-image:$project.version"]
}
tasks.named("dockerBuildNative") {
images = ["eu-frankfurt-1.ocir.io/xyzzyz/repo/my-image-native:$project.version"]
}
tasks.named("dockerBuildCrac") {
images = ["eu-frankfurt-1.ocir.io/xyzzyz/repo/my-image-crac:$project.version"]
}
tasks.named<DockerBuildImage>("dockerBuild") {
images.add("eu-frankfurt-1.ocir.io/xyzzyz/repo/my-image:$project.version")
}
tasks.named<DockerBuildImage>("dockerBuildNative") {
images.add("eu-frankfurt-1.ocir.io/xyzzyz/repo/my-image-native:$project.version")
}
tasks.named<DockerBuildImage>("dockerBuildCrac") {
images.add("eu-frankfurt-1.ocir.io/xyzzyz/repo/my-image-crac:$project.version")
}
Notice that you can supply two different image names to push to for the JVM version and the native version of the application.
6.4.2. Customized docker files
If you wish to customize the docker builds that are used, the easiest way is to run ./gradlew dockerfile
(or dockerfileNative
for the native version) and copy the generated Dockerfile
from build/docker
to your root directory and modify as required.
To customize the JVM arguments or native executable arguments, use the args
method of the dockerfile
and dockerfileNative
tasks:
tasks.named("dockerfile") {
args("-Xmx128m")
}
tasks.named("dockerfileNative") {
args("-Xmx64m")
}
tasks.named<MicronautDockerfile>("dockerfile") {
args("-Xmx128m")
}
tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
args("-Xmx64m")
}
The above configuration uses a max heap setting of 128m
for Java and 64m
for native executable for the application.
6.4.3. GraalVM JDK version
By default, the dockerfileNative
task will create a dockerfile that uses the Graal JDK base image with the current JDK version.
To build a native executable inside docker with a specific GraalVM JDK version, you can use the jdkVersion
property of the dockerfileNative
task:
dockerfileNative {
jdkVersion = '21'
}
tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
jdkVersion.set("21")
}
6.4.4. Adding additional instructions
To add additional docker instructions to the generated Dockerfile, such as adding a HEALTHCHECK, you can do the following. The additional instructions will be added at the end of the Dockerfile
just before the ENTRYPOINT
.
tasks.named("dockerfile") {
args("-Xmx128m")
instruction """HEALTHCHECK CMD curl -s localhost:8090/health | grep '"status":"UP"' """
}
tasks.named("dockerfileNative") {
args("-Xmx64m")
instruction """HEALTHCHECK CMD curl -s localhost:8090/health | grep '"status":"UP"'"""
}
tasks.named<Dockerfile>("dockerfile") {
args("-Xmx128m")
instruction("""HEALTHCHECK CMD curl -s localhost:8090/health | grep '"status":"UP"' """)
}
tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
args("-Xmx64m")
instruction("""HEALTHCHECK CMD curl -s localhost:8090/health | grep '"status":"UP"'""")
}
You can also add any of the other instructions/commands that the docker plugin supports, see the Dockerfile task documentation.
6.4.5. Tweaking the generated docker files
In case adding instructions doesn’t generate the expected output, for example because of ordering issues, or because of multi-level docker files, an API will let you modify the generated files.
For example, to customize the file generated by the dockerfile
task, you can do the following:
tasks.named("dockerfile") {
editDockerfile {
after('COPY --link layers/libs /home/app/libs') {
insert('COPY --link server.iprof /home/app/server.iprof')
}
}
}
tasks.named<io.micronaut.gradle.docker.DockerBuildOptions>("dockerfile") {
editDockerfile {
after("COPY --link layers/libs /home/app/libs") {
insert("COPY --link server.iprof /home/app/server.iprof")
}
}
}
The editDockerfile
DSL allows you to:
-
narrow down the scope of the lines to edit by using
before
andafter
methods -
insert lines by calling
insert
-
replace lines by calling
replace
6.4.6. Duplicates on classpath
In some projects, it may happen that different transitive dependencies have the same file name. In this case, the task which builds the layers will fail with a duplicate error. You can work around this issue by configuring the duplicates strategy on the task:
tasks.withType<io.micronaut.gradle.docker.tasks.BuildLayersTask> {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
tasks.withType(io.micronaut.gradle.docker.tasks.BuildLayersTask) {
duplicatesStrategy.set(DuplicatesStrategy.INCLUDE)
}
6.5. Micronaut Runtimes
A higher level concept of "runtimes" is included in the Micronaut Gradle plugin which essentially allows the plugin to decide which server runtime to include in the dependencies of the application when building the application. For example consider this minimal build:
plugins {
id 'io.micronaut.application' version '4.4.4'
}
version "0.1"
group "com.example"
repositories {
mavenCentral()
}
micronaut {
version = "4.6.3"
runtime "netty"
}
dependencies {
runtimeOnly("ch.qos.logback:logback-classic")
}
application {
mainClass = "example.Application"
}
plugins {
id("io.micronaut.application") version "4.4.4"
}
version = "0.1"
group = "com.example"
repositories {
mavenCentral()
}
micronaut {
version.set("4.6.3")
runtime.set("netty")
}
dependencies {
runtimeOnly("ch.qos.logback:logback-classic")
}
application {
mainClass = "example.Application"
}
Here the only dependency declared is on the logging framework to use however runtime
is to netty
resulting in an application that can be built and run.
If you wish to take the same and build or run it with a different runtime you can pass the micronaut.runtime
property for the build. For example:
./gradlew run -Pmicronaut.runtime=google_function
The above example run the application as a Google Cloud Function.
The available runtimes are:
-
netty
- A Netty server runtime -
jetty
- A Jetty server runtime -
tomcat
- A Tomcat server runtime -
undertow
- An Undertow server runtime -
lambda
- Allows building the application into an AWS Lambda -
oracle_function
- A Project.fn runtime for deploying Oracle Functions -
google_function
- A runtime for deploying Google Functions. -
azure_function
- A runtime for deploying Azure Functions -
http_poja
- A plain old java application runtime based on Apache libraries
The advantage of allowing your dependencies to be dictated by the runtime is that you can potentially take the same application and deploy it to any of the above runtimes without changes.
6.5.1. Deploying to AWS Lambda as GraalVM native executable
If you are interested in deploying your Micronaut application to AWS Lambda using GraalVM, you only need to set the runtime to lambda
and execute ./gradlew buildNativeLambda
.
This task will generate a GraalVM native executable inside a Docker container, and create the file build/libs/your-app.zip
file ready to be deployed to AWS Lambda using a custom runtime. See more information in Micronaut AWS documentation.
Architecture considerations
The plugin will detect the host operating system architecture (based on the os.arch
Java system property) and will install the corresponding GraalVM binary distribution inside the Docker image.
This means that when running packaging from an X86_64 (Intel/AMD) machine, the produced native executable will be an amd64
binary, whilst on an ARM host (such as the new Mac M1) it will be an aarch64
binary.
To override this automatic selection, you can configure the graalArch
property in a dockerfileNative
configuration block in your build:
dockerfileNative {
graalArch.set("x64")
}
The compiled native binary will ultimately be copied into an cgr.dev/chainguard/wolfi-base:latest
base image.
These base images can be overridden via configuration see Base image and pull limits and Build "mostly static" native executables.
Base image and pull limits
By default, the plugin will use amazonlinux pulled from Docker Hub as a base image for GraalVM native AWS Lambda executables.
If you wish to switch to using a different base image (for example to use your own Docker repository or to use Amazon’s public repository), you can configure the baseImage
property in a dockerfileNative
configuration block in your build:
dockerfileNative {
// Use the image from the Amazon ECR Public Gallery
baseImage.set("public.ecr.aws/amazonlinux/amazonlinux:2023")
}
6.6. Packaging the application
By default, the plugin doesn’t create a runnable fatjar when running ./gradlew assemble
.
There are a couple of options:
6.6.1. Layered application
The plugin creates a "layered" application in build/docker/main/layers
and from that directory you can run java -jar myapp.jar
.
It works because that directory contains a lib
directory with all the libraries and a resources
directory with the configuration.
Keep in mind that copying the only .jar
file to another directory won’t work.
7. Micronaut GraalVM Plugin
The Micronaut GraalVM plugin is applied automatically by the
Micronaut application plugin (see below),
and it provides tasks to generate a GraalVM native executable and also creates the GraalVM resource-config.json
automatically with all the resources from the application.
This plugin can be applied separately if you use the application
plugin without the io.micronaut.application
plugin (but we strongly recommend to switch to the io.micronaut.application
plugin in this case).
8. Micronaut AOT Plugin
Warning
|
The Micronaut AOT module is in experimental stages. Use at your own risk! |
The io.micronaut.aot
plugin provides integration with Micronaut AOT.
Micronaut AOT is a module which aims at pre-computing a number of things at build time in order to provide faster startup times and smaller binaries.
At the moment, the plugin supports optimizing Micronaut applications only (Micronaut libraries or functions will be supported in a future release).
It is capable of generating a number of things:
-
an optimized jar, which is a jar corresponding to the regular application jar, except that it contains some optimizations computed at build time. It may contain, for example, additional classes, or even have different resources.
-
an optimized fat jar, which is the same as the previous one, except that it also embeds all transitive dependencies and is a standalone executable.
-
an optimized native binary which is a GraalVM executable compiled with Micronaut AOT optimizations
-
an optimized docker image which is a Docker image containing the optimized application
-
an optimized native docker image which is a Docker image containing the optimized application compiled as a native executable
Important
|
Micronaut AOT is a deployment optimization: it adds to build time, in order to make the final application faster to start, or the native executables smaller. Therefore, if you use the AOT tasks during development, your feedback cycle will be slower (but the application will start faster). It is a good idea, however, to check the result of the optimization locally, similarly to what you’d do for a native executable. |
8.1. Configuration
The io.micronaut.aot
plugin is an extension to the io.micronaut.application
plugin.
plugins {
...
id "io.micronaut.application" version "4.4.4"
id "io.micronaut.aot" version "4.4.4"
...
}
plugins {
...
id("io.micronaut.application") version "4.4.4"
id("io.micronaut.aot") version "4.4.4"
...
}
This will add an aot
DSL block to the micronaut
extension, which can be used to enable optimizations:
micronaut {
...
aot {
// optional, override the Micronaut AOT version
version = "1.0.1"
// optimizations configuration
optimizeServiceLoading = true
convertYamlToJava = true
precomputeOperations = true
cacheEnvironment = true
netty {
enabled = true
}
}
}
micronaut {
...
aot {
// optional, override the Micronaut AOT version
version.set("1.0.0")
// optimizations configuration
optimizeServiceLoading.set(true)
convertYamlToJava.set(true)
precomputeOperations.set(true)
cacheEnvironment.set(true)
netty {
enabled.set(true)
}
}
}
In addition, you can use the aotPlugins
configuration to declare additional AOT modules to be used:
dependencies {
aotPlugins 'io.micronaut.security:micronaut-security-aot:1.0.0'
}
dependencies {
aotPlugins("io.micronaut.security:micronaut-security-aot:1.0.0")
}
Because Micronaut AOT is an extensible optimization engine, not all optimizations are known beforehand by the plugin, which means that not all of them may be accessible via the DSL. For this reason, it is possible to provide a Micronaut AOT configuration file instead:
micronaut {
...
aot {
configFile = file("gradle/micronaut-aot.properties")
}
}
micronaut {
...
aot {
configFile.set(file("gradle/micronaut-aot.properties"))
}
}
Note
|
You can provide both a configuration file and aot DSL optimizations.
The configuration will be merged, by reading the file first, then using the DSL options.
|
If you want to know about all possible optimizations, you can run the createAotSampleConfigurationFiles
which will generate a couple of sample files:
The build/generated/aot/samples/jit/jit.properties
will contain the optimizations which are relevant to an application running in the regular Java virtual machine, for example:
# Checks of existence of some types at build time instead of runtime known.missing.types.enabled = true # A list of types that the AOT analyzer needs to check for existence (comma separated) known.missing.types.list = javax.inject.Inject,io.micronaut.SomeType # Replaces logback.xml with a pure Java configuration (NOT YET IMPLEMENTED!) logback.xml.to.java.enabled = true # Precomputes Micronaut configuration property keys from the current environment variables precompute.environment.properties.enabled = true # Scans reactive types at build time instead of runtime scan.reactive.types.enabled = true # Caches environment property values: environment properties will be deemed immutable after application startup. cached.environment.enabled = true # Scans for service types ahead-of-time, avoiding classpath scanning at startup serviceloading.jit.enabled = true # The list of service types to be scanned (comma separated) service.types = io.micronaut.Service1,io.micronaut.Service2 # A list of implementation types which shouldn't be included in the final application (comma separated) serviceloading.rejected.impls = com.Misc,org.Bar # Converts YAML configuration files to Java configuration yaml.to.java.config.enabled = true # Precomputes property sources at build time sealed.property.source.enabled = true
Another file, build/generated/aot/samples/native/native.properties
will contain the same, but with the options which are relevant to an application compiled to a native executable:
# Generates GraalVM configuration files required to load the AOT optimizations graalvm.config.enabled = true # The list of service types to be scanned (comma separated) service.types = io.micronaut.Service1,io.micronaut.Service2 # Checks of existence of some types at build time instead of runtime known.missing.types.enabled = true # A list of types that the AOT analyzer needs to check for existence (comma separated) known.missing.types.list = javax.inject.Inject,io.micronaut.SomeType # Replaces logback.xml with a pure Java configuration (NOT YET IMPLEMENTED!) logback.xml.to.java.enabled = true # Precomputes Micronaut configuration property keys from the current environment variables precompute.environment.properties.enabled = true # Scans reactive types at build time instead of runtime scan.reactive.types.enabled = true # Caches environment property values: environment properties will be deemed immutable after application startup. cached.environment.enabled = true # Scans for service types ahead-of-time, avoiding classpath scanning at startup serviceloading.native.enabled = true # The list of service types to be scanned (comma separated) service.types = io.micronaut.Service1,io.micronaut.Service2 # A list of implementation types which shouldn't be included in the final application (comma separated) serviceloading.rejected.impls = com.Misc,org.Bar # Converts YAML configuration files to Java configuration yaml.to.java.config.enabled = true # Precomputes property sources at build time sealed.property.source.enabled = true
For native executables, it is important to always have the graalvm.config.enabled
option set to true
, otherwise the AOT optimizations will not be loaded. The plugin takes care of setting this flag to true
for you.
It is important to understand that Micronaut AOT works at build time. Therefore, some optimizations like conversion of YAML files to Java configuration will effectively disable the ability to change the configuration at runtime.
8.2. Running an optimized application
The plugin provides a couple of tasks aimed at running an optimized application.
The first one, optimizedJar
, will simply run the AOT compiler and produce an "optimized" jar.
If you want to run the application with the resulting jar, you will need to call the optimizedRun
task instead, which will create the jar and then start the application.
If you also have the distribution
plugin applied, the optimized jar will be used to create optimized distributions, in which case you can call the optimizedDistZip
task to create a distribution zip, the optimizedDistTar
to create an optimized distribution tar file, or installOptimizedDist
to install the optimized application to the build/install
directory.
8.3. Running an optimized fat jar
The plugin supports building an optimized fat jar. You will need to apply the shadow
plugin to enable this feature:
plugins {
...
id "com.github.johnrengelman.shadow" version "8.1.1"
...
}
plugins {
...
id("com.github.johnrengelman.shadow") version "8.1.1"
...
}
Then you can generate the fat jar by calling: ./gradlew optimizedJitJarAll
.
The task will generate a fat jar in the build/libs
directory, that you can run using:
java -jar build/libs/myapp-0.1-all-optimized.jar
8.4. Building and running an optimized native application
The plugin creates a new native binary called optimized
.
The GraalVM plugin will then automatically create a couple of tasks for you:
-
the
nativeOptimizedCompile
task will compile a native executable with the AOT optimizations -
the
nativeOptimizedRun
task will run the optimized native executable (you can call this task directly, it will precompile the native executable before)
8.5. Building an optimized Docker image
It is also possible to build an optimized application and package it into a Docker image.
For this, you need to call ./gradlew optimizedDockerBuild
.
It will produce a docker image that you can start using docker run
.
Alternatively, you can call ./gradlew optimizedDockerPush
to push the generated image to your docker registry.
All configuration options which apply to the standard docker image are also available to the optimized Docker images.
8.6. Building an optimized native Docker image
This task also produces a Docker image, but it will build a native image containing the optimized application within a container, in order to produce a Docker image which runs the optimized application natively.
The 2 tasks which are available for this are:
-
optimizedDockerBuildNative
to build the optimized native Docker image -
optimizedDockerPushNative
to push the optimized native Docker image
9. Automatic test resources provisioning
The Micronaut Gradle plugin integrates with Micronaut Test Resources to automatically provision test resources. In particular, it makes use of Testcontainers to automatically provide resources like databases or other external services like Kafka or RabbitMQ.
Test resources are handled by a "test resources service" which is a server accepting requests for test resources, which lifecycle is handled by the Gradle plugin.
A test resources request is, to simplify, a request to resolve a missing configuration property.
For example, if the kafka.bootstrap-servers
property isn’t set, Micronaut will query the test resources service for the value of this property.
This will trigger the creaton of a Kafka test container, and the service will answer with the URI to the bootstrap server.
Note
|
The test resources service makes use of Class Data Sharing on Java 17+ in order to make startup faster. In general, this shouldn’t be a problem but there may be cases where the test resources service fails to load because of a bug in the JDK. Should this happen to you, you can disable class data sharing explicitly using this configuration:
|
9.1. The test resources plugin
The easiest way to add test resources support is to apply the io.micronaut.test-resources
plugin:
plugins {
id "io.micronaut.application" version "4.6.3"
id "io.micronaut.test-resources" version "4.6.3"
...
}
plugins {
id("io.micronaut.application") version "4.6.3"
id("io.micronaut.test-resources") version "4.6.3"
...
}
Adding this plugin will be sufficient for most use cases.
This will let Gradle automatically start containers, for example, when a particular property is not present in your configuration and that you run tests (./gradlew test
) or the application in development mode (./gradlew run
or ./gradlew -t run
).
For example, if the datasources.default.url
configuration property is missing, and that your configuration contains:
datasources:
default:
dialect: MYSQL
Then a MySQL database will automatically be started and available for use in your tests: the url
, username
and password
configuration properties will automatically be injected to your application.
Please refer to the Micronaut Test Resources documentation for a list of all supported modules and their configuration options.
9.2. Configuring the test resources plugin
In addition, the plugin will add a testResources
extension to the micronaut
configuration block, providing a number of options:
micronaut {
testResources {
enabled = true // true by default
version = "1.0.0" // override Micronaut Test Resources version
explicitPort = 15471 // by default, uses a random port
inferClasspath = true // true by default
additionalModules.add(JDBC_MYSQL) // empty by default
clientTimeout = 60 // in seconds, maximum time to wait for resources to be available, 60s by default
serverIdleTimeoutMinutes = 60 // if the server doesn't receive any request for this amount of time, it will be shut down
sharedServer = true // false by default
sharedServerNamespace = 'custom' // unset by default
}
}
micronaut {
testResources {
enabled.set(true) // true by default
version.set("1.0.0") // override Micronaut Test Resources version
explicitPort.set(15471) // by default, uses a random port
inferClasspath.set(true) // true by default
additionalModules.add(JDBC_MYSQL) // empty by default
clientTimeout.set(60) // in seconds, maximum time to wait for resources to be available, 60s by default
sharedServer.set(true) // false by default
sharedServerNamespace.set("custom") // unset by default
}
}
-
the
version
property will let you override the default version of Micronaut Test Resources that the plugin uses. -
the
enabled
property can let you disable test resources provisioning (for example depending on the environment). -
by default, a test resources server will be started. This server uses a randomly available port, but you can override this by setting the
explicitPort
property instead. -
by default, the plugin will automatically determine which modules to add to the test resources support, by inspecting the dependencies declared in the project. For example, if you use both
micronaut-data-jdbc
andmysql-connector-java
, it will automatically add support for MySQL provisioning. Under some circumstances, you might want to disable this behavior by setting theinferClasspath
property tofalse
. -
the
additionalModules
property can be used to explicitly declare test resources modules to be loaded. This is useful if inference failed to detect a module, or if you want to use a standalone test resources service. -
if set to
true
, then the test server which is used by the project can be shared between independent builds (e.g. different Git repositories): this can be useful in conjunction with a standalone test resources service, where for example a producer is used in one project, and a consumer is defined in another, but both need to use the same messaging server. -
if set, the
sharedServerNamespace
property will let you declare that the shared test resources service but be executed in a particular namespace. This can be useful if you need multiple shared servers (the default assumes a single shared server)
Note
|
The
|
9.3. Using test resources with native binaries
By default, native binaries are considered production code. This implies that the test resources client will not be included in the generated native binaries and therefore, by default, native applications will not make use of test resources.
In order to produce a native binary which is capable of using test resources, you must explicitly pass a system property to the build:
./gradlew nativeCompile -Dtestresources.native=true
or, if you want to run the binary directly:
./gradlew nativeRun -Dtestresources.native=true
Note that you don’t need to do this for the nativeTest
task, which is automatically configured to use test resources.
9.4. Standalone test resources service
The io.micronaut.test-resources
plugin works particularly well in single-module projects, or in multi-projects where test resources are not shared between modules.
However, in some cases, you may want to reuse test containers for multiple projects of the same multi-project build, in order to save startup times for example.
For this purpose, the io.micronaut.test-resources
plugin can be applied on a project independently of the application or library plugins.
For example, imagine a multi-project build which consists of :
-
a Kafka consumer
-
a Kafka producer
-
functional tests integrating both
If each project applies the io.micronaut.test-resources
plugin independently, then each project will use its own test server, independently of the others.
However, you might want to run the consumer in one terminal, and the producer in another, and still want them to talk to the same Kafka cluster. For this you have a couple options:
-
you can use a shared test server, by setting the
sharedServer
property totrue
in thetestResources
extension. -
you can define a distinct project which role is to handle the test resources lifecycle
The first solution comes with a major drawback: shared servers are shared by all projects in the multi-project build, but also between projects of the same build. However, the configuration of the server will depend on the first one started.
To avoid this problem it is recommended to declare a distinct project to handle test resources.
Here, we’re going to add a project called shared-testresources
which is going to be a test resources provider:
// shared-testresources/build.gradle
plugins {
id "io.micronaut.test-resources" version "4.6.3" // (1)
}
micronaut {
testResources {
additionalModules.add(KAFKA) // (2)
}
}
// shared-testresources/build.gradle.kts
plugins {
id("io.micronaut.test-resources") version "4.6.3" // (1)
}
micronaut {
testResources {
additionalModules.add(KAFKA) // (2)
}
}
-
use
test-resources
as a standalone plugin -
declare that it will provide Kafka containers
Then each consumer of test resources need to apply the io.micronaut.test-resources-consumer
plugin instead:
plugins {
id "io.micronaut.application" version "4.6.3"
id "io.micronaut.test-resources-consumer" version "4.6.3" // (1)
}
dependencies {
testResourcesService project(':shared-testresources') // (2)
}
plugins {
id("io.micronaut.application") version "4.6.3"
id("io.micronaut.test-resources-consumer") version "4.6.3" // (1)
}
dependencies {
testResourcesService(project(":shared-testresources")) // (2)
}
-
Use the test resources consumer plugin instead
-
Declare that it consumes test resources provided by the
:testresources
project
Now Gradle will automatically start test resources when one of the project needs them, and reuse them in all consumer projects.
9.5. Test resources lifecycle
Test resources are handled by a service which is, by default, started at the beginning of a build, and stopped at the end.
For example, if you invoke:
./gradlew test
Then the test resources service will be started before tests are executed, then tests will share the resources provided by the service. Any test resource started during the test will be stopped when the build finishes.
In continuous mode, the test resources are shared between builds. For example, if you run:
./gradlew -t test
(run test in continuous mode)
Then the test resources will be spawned during the first execution of tests. If you make any change to sources (production or test) and save the files, Gradle will rebuild the project and run the tests using the same test resources. This behavior can be extremely useful to save time since typically Docker containers would only be spawned once. If you interrupt the continuous build, the test resources will be stopped.
Keeping test resources alive
We have seen that running in continous mode allows keeping test resources alive as long as a continous build runs. However, what if you want to start the test resources service in the background, and keep it alive for multiple, independent builds (different invocations on the command line for example) ?
You can achieve this behavior by running the startTestResourcesService
command:
./gradlew startTestResourcesService
This command must be the only command executed: it will start a test resources service in the background, which will be shared between builds. Therefore, it’s your responsibility to stop the service when you are done by running:
./gradlew stopTestResourcesService
Warning
|
When keeping a test resources service alive, you must understand that any change to the test resources configuration will be ignored until you stop the service. |
Implementing your own test resources resolver
Micronaut Test Resources provides integration with a lot of databases and services, and also supports a fully declarative way to spawn test containers. However, there are cases where you might need to implement your own test resources resolver.
The plugin makes it extremely straightforward: it declares an additional source set called testResources
.
You therefore write your test resources directly in src/testResources/java
for example, and the test resource will be automatically made available.
For example, let’s write a test resource which provides the value of the greeting.message
property.
First, let’s create the src/testResources/java/demo/GreetingTestResource.java
file:
package demo;
import io.micronaut.testresources.core.TestResourcesResolver;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
public class GreetingTestResource implements TestResourcesResolver {
public static final String PROPERTY = "greeting.message";
@Override
public List<String> getResolvableProperties(Map<String, Collection<String>> propertyEntries, Map<String, Object> testResourcesConfig) {
return Collections.singletonList(PROPERTY);
}
@Override
public Optional<String> resolve(String propertyName, Map<String, Object> properties, Map<String, Object> testResourcesConfiguration) {
if (PROPERTY.equals(propertyName)) {
return Optional.of(StringUtils.capitalize("hello from my test resource!"));
}
return Optional.empty();
}
}
Then you need to declare the test resource in the service file descriptor:
demo.GreetingTestResource
Now the value of the greeting.message
property will be available in your tests:
@MicronautTest
class DemoTest {
@Value("${greeting.message}")
String greeting;
@Test
void testItWorks() {
assertEquals("Hello from my test resource!", greeting);
}
}
You can learn more about custom test resource resolvers in the Micronaut Test Resources documentation.
10. Micronaut CRaC Plugin
Warning
|
The Micronaut CRaC module is in experimental stages. Use at your own risk! |
The io.micronaut.crac
plugin provides extra integration with Micronaut CRaC.
Micronaut CRaC is a module which adds support for Coordinated Restore at Checkpoint to Micronaut applications.
It is capable of generating a docker image containing a CRaC enabled JDK and a pre-warmed, checkpointed application via a new task dockerBuildCrac
.
Important
|
Currently, CRaC support has only been tested on Ubuntu 18.04, 20.04 and 22.04. |
When executed, this task will:
-
Create a layered docker image containing the latest CRaC enabled JDK and your application.
-
Wait for it to start
-
Run a warmup script to get the instance of your application "hot".
-
Take a checkpoint of the application, and copy this locally.
-
Create a new docker image containing the CRaC enabled JDK your application and the checkpoint files.
You will then be able to run your image via:
docker run --cap-add=cap_sys_ptrace -p 8080:8080 <image-name>
10.1. Configuration
The io.micronaut.crac
plugin is a standalone plugin which requires the following minimal configuration.
plugins {
...
id "io.micronaut.minimal.application" version "4.4.4"
id "io.micronaut.docker" version "4.4.4"
id "io.micronaut.crac" version "4.4.4"
...
}
plugins {
...
id("io.micronaut.minimal.application") version "4.4.4"
id("io.micronaut.docker") version "4.4.4"
id("io.micronaut.crac") version "4.4.4"
...
}
This will add a crac
DSL block to the micronaut
extension, which can be used to configure the image building:
micronaut {
...
crac {
// is the plugin enabled or not
enabled = true
// the base docker image to use for the Checkpoint and final images
baseImage = "ubuntu:22.04"
// The platform to use pulling the base image. Defaults to none specified. Deprecated, use arch instead.
platform = "linux/amd64"
// The architecture of the CRaC JDK to use. Defaults to the architecture of the machine.
// (currently only 'aarch64' or 'amd64' are supported)
arch = "aarch64"
// The OS of the Azul CRaC JDK to use. Defaults to linux-glibc for the default base image.
os = "linux-glibc"
// The version of the Azul CRaC JDK to use in the image (currently only 17 is supported)
javaVersion = JavaLanguageVersion.of(17)
// A command to run that will be successful when the application is ready to be checkpointed.
preCheckpointReadinessCheck = "curl --output /dev/null --silent --head http://localhost:8080"
// The existing Docker network to use when running the application prior to checkpointing
network = "my-docker-network"
// You can use these to replace the script that generates a checkpoint, and the script that warms up the
// application prior to checkpointing
warmupScript = file("customWarmupScript.sh")
checkpointScript = file("customCheckpointScript.sh")
}
}
micronaut {
...
crac {
// is the plugin enabled or not
enabled.set(true)
// the base docker image to use for the Checkpoint and final images
baseImage.set("ubuntu:22.04")
// The platform to build the image for (currently must be linux/amd64). Deprecated, use arch instead.
platform.set("linux/amd64")
// The architecture of the CRaC JDK to use. Defaults to the architecture of the machine.
// (currently only 'aarch64' or 'amd64' are supported)
arch.set("aarch64")
// The OS of the Azul CRaC JDK to use. Defaults to linux-glibc for the default base image.
os.set("linux-glibc")
// The version of the Azul CRaC JDK to use in the image (currently only 17 is supported)
javaVersion.set(JavaLanguageVersion.of(17))
// A command to run that will be successful when the application is ready to be checkpointed.
preCheckpointReadinessCheck.set("curl --output /dev/null --silent --head http://localhost:8080")
// The existing Docker network to use when running the application prior to checkpointing
network.set("my-docker-network")
// You can use these to replace the script that generates a checkpoint, and the script that warms up the
// application prior to checkpointing
warmupScript.set(file("customWarmupScript.sh"))
checkpointScript.set(file("customCheckpointScript.sh"))
}
}
11. OpenAPI code generation
The io.micronaut.openapi
plugin adds support for generating OpenAPI clients or servers, given an OpenAPI definition, in both Java and Kotlin.
The plugin adds an openapi extension to the micronaut
configuration block.
11.1. Generating a client
You can generate a client by configuring the extension via the client { … }
block:
micronaut {
...
openapi {
client(file("src/openapi/my-definition.yml")) {
apiPackageName = "com.mycompany.api"
modelPackageName = "com.mycompany.model"
useOptional = true
clientId = "some-client-id"
}
}
}
micronaut {
...
openapi {
client(file("src/openapi/my-definition.yml")) {
apiPackageName.set("com.mycompany.api")
modelPackageName.set("com.mycompany.model")
useOptional.set(true)
clientId.set("some-client-id")
// Supports Kotlin codegen too
lang.set("kotlin")
}
}
}
The generated sources will be found by default in your $buildDir/generated/openapi/client
directory, and automatically added to your main source set (so the classes are directly available for you to use).
You can generate multiple clients by adding multiple client
blocks, in which case each client is identified by a name:
micronaut {
...
openapi {
client("first", file("src/openapi/my-definition.yml")) {
apiPackageName = "com.mycompany.api"
modelPackageName = "com.mycompany.model"
useOptional = true
clientId = "some-client-id"
}
client("second", file("src/openapi/other-definition.yml")) {
apiPackageName = "com.mycompany.other.api"
modelPackageName = "com.mycompany.other.model"
useOptional = true
clientId = "some-other-client-id"
}
}
}
micronaut {
...
openapi {
client("first", file("src/openapi/my-definition.yml")) {
apiPackageName.set("com.mycompany.api")
modelPackageName.set("com.mycompany.model")
useOptional.set(true)
clientId.set("some-client-id")
// Supports Kotlin codegen too
lang.set("kotlin")
}
client("second", file("src/openapi/other-definition.yml")) {
apiPackageName.set("com.mycompany.other.api")
modelPackageName.set("com.mycompany.other.model")
useOptional.set(true)
clientId.set("other-client-id")
// Supports Kotlin codegen too
lang.set("kotlin")
}
}
}
Please refer to OpenApiClientSpec for the whole list of client configuration options.
11.2. Generating a server
You can generate a server by configuring the extension via the server { … }
block:
micronaut {
...
openapi {
server(file("src/openapi/my-definition.yml")) {
apiPackageName = "com.mycompany.api"
modelPackageName = "com.mycompany.model"
controllerPackage = "com.mycompany.controller"
}
}
}
micronaut {
...
openapi {
server(file("src/openapi/my-definition.yml")) {
apiPackageName.set("com.mycompany.api")
modelPackageName.set("com.mycompany.model")
controllerPackage.set("com.mycompany.controller")
// Supports Kotlin codegen too
lang.set("kotlin")
}
}
}
The generated sources will be found by default in your $buildDir/generated/openapi/server
directory, and automatically added to your main source set (so the classes are directly available for you to use).
You can generate multiple servers by providing a name to the server
block:
micronaut {
...
openapi {
server("first", file("src/openapi/my-definition.yml")) {
apiPackageName = "com.mycompany.api"
modelPackageName = "com.mycompany.model"
controllerPackage = "com.mycompany.controller"
}
server("second", file("src/openapi/other-definition.yml")) {
apiPackageName = "com.mycompany.other.api"
modelPackageName = "com.mycompany.other.model"
controllerPackage = "com.mycompany.other.controller"
}
}
}
micronaut {
...
openapi {
server("first", file("src/openapi/my-definition.yml")) {
apiPackageName.set("com.mycompany.api")
modelPackageName.set("com.mycompany.model")
controllerPackage.set("com.mycompany.controller")
// Supports Kotlin codegen too
lang.set("kotlin")
}
server("second", file("src/openapi/other-definition.yml")) {
apiPackageName.set("com.mycompany.other.api")
modelPackageName.set("com.mycompany.other.model")
controllerPackage.set("com.mycompany.other.controller")
// Supports Kotlin codegen too
lang.set("kotlin")
}
}
}
Note
|
Server generation will generate interfaces that you have to implement in order to write your server code. |
Please refer to OpenApiServerSpec for the whole list of client configuration options.
11.3. Advanced configuration
The Micronaut OpenAPI plugin lets you override the default Micronaut OpenAPI version to use.
To do this, set the version in the openapi
extension:
micronaut {
...
openapi {
version = "5.0.2"
}
}
micronaut {
...
openapi {
version.set("5.0.2")
}
}
In addition, it exposes a openApiGenerator
configuration which can be used to declare additional dependencies to put on the generator classpath.
This can be useful in case you want to implement your own generators, in which case you will also have to implement custom tasks which extend the AbstractOpenApiGenerator task type.
12. Additional Notes
12.1. Automatic annotationProcessor dependencies
When the plugin detects you have a dependency with a group id corresponding to a known annotation processor for it, it adds the annotation processor automatically. The following annotation processors are currently supported by this feature.
-
io.micronaut.data:micronaut-data-processor
-
io.micronaut.jaxrs:micronaut-jaxrs-processor
-
io.micronaut.micrometer:micronaut-micrometer-annotation
-
io.micronaut.microstream:micronaut-microstream-annotations
-
io.micronaut.openapi:micronaut-openapi
-
io.micronaut.security:micronaut-security-annotations
-
io.micronaut.serde:micronaut-serde-processor
-
io.micronaut.spring:micronaut-spring-annotation
-
io.micronaut.tracing:micronaut-tracing-annotation
-
io.micronaut.validation:micronaut-validation-processor
12.2. Suppressing automatic dependencies
In some circumstances, automatic dependencies – e.g. annotation processors listed above – can get in the way. This should be rare, but it is possible to suppress them, as follows. It has no default and using suppression shifts responsibility of adding the dependencies to the user.
micronaut {
// The Micronaut plugins can automatically add dependencies to your project. If, for some reason,
// a dependency shouldn't be automatically added, you can add its coordinates to this set.
// The format is "group:name". It must not include the version.
ignoredAutomaticDependencies.add("io.micronaut.data:micronaut-data-processor")
}
micronaut {
// The Micronaut plugins can automatically add dependencies to your project. If, for some reason,
// a dependency shouldn't be automatically added, you can add its coordinates to this set.
// The format is "group:name". It must not include the version.
ignoredAutomaticDependencies.add("io.micronaut.data:micronaut-data-processor")
}
13. Upgrade notes
13.1. Upgrading from 2.x
When upgrading from the 2.x version of the plugins, you will need to change the configuration of the GraalVM native executable builds if you use them.
Typically, instead of configuring executable compilation using the task:
nativeImage {
imageName.set("custom")
}
You now need to use the graalvmNative
extension. This extension supports building multiple native executables, and the main one is named main
(there is another one for tests, called test
, which runs unit tests natively):
graalvmNative {
binaries {
named("main") {
imageName.set("custom")
}
}
}
Similarly, to compile the native executable, you now need to run nativeCompile
instead of nativeImage
.
In addition, the official GraalVM plugin makes use of Gradle toolchains support, which can lead to surprising behavior if you are used to switching between local JDKs. If you are facing errors like this one:
> No compatible toolchains found for request filter: {languageVersion=17, vendor=matching('GraalVM'), implementation=vendor-specific} (auto-detect true, auto-download true)
then we recommend tweaking toolchain detection as described in this section of the documentation.
In any case, make sure to follow the configuration instructions.