Micronaut NullAway and JSPecify
In this blog post, I show you how to configure a Micronaut 4 application to use JSpecify and NullAway to protect you from NPEs (Null Pointer Exceptions).
NullAway:
NullAway is a tool to help eliminate NullPointerExceptions (NPEs) in your Java code. To use NullAway, first add @Nullable annotations in your code wherever a field, method parameter, or return value may be null. Given these annotations, NullAway performs a series of type-based, local checks to ensure that any pointer that gets dereferenced in your code cannot be null.
JSpecify Dependency
Since Micronaut 4.10, Micronaut BOM contains the JSpecify dependency. Thus, you can add the following dependency to your project without specifying the version:
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
</dependency>
NullAway with Gradle
GitHub Repository with a Gradle Micronaut Application which uses NullAway
To use NullAway in a Gradle Micronaut Application. You need to add the Gradle error prone plugin and such configuration:
plugins {
...
..
.
id("net.ltgt.errorprone") version "4.3.0"
}
dependencies {
...
..
.
implementation("org.jspecify:jspecify")
errorprone("com.uber.nullaway:nullaway:0.12.12")
errorprone("com.google.errorprone:error_prone_core:2.44.0")
}
tasks.withType<JavaCompile>().configureEach {
options.errorprone {
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", example.micronaut")
if (name.lowercase().contains("test")) {
disable("NullAway")
}
}
}
check("NullAway", CheckSeverity.ERROR)setsNullAwayissues to the error level.option("NullAway:AnnotatedPackages", “example.micronaut”)tellsNullAwaythat source code in packages under theexample.micronautnamespace should be checked for null dereferences and proper usage of@Nullableannotations, and that class files in these packages should be assumed to have correct usage of@Nullable.- Then, it disables
NullAwayon test code.
These instructions are described in the NullAway README.md file.
NullAway with Maven
GitHub Repository with a Maven Micronaut Application which uses NullAway
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...
..
.
<properties>
...
..
.
<error-prone.version>2.29.2</error-prone.version>
<nullaway.version>0.12.12</nullaway.version>
</properties>
...
..
.
<dependencies>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>
</dependency>
...
..
.
</dependencies>
<build>
<plugins>
...
..
.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-XDcompilePolicy=simple</arg>
<arg>--should-stop=ifError=FLOW</arg>
<arg>-Xplugin:ErrorProne -Xep:NullAway:ERROR -XepOpt:NullAway:AnnotatedPackages=example.micronaut</arg>
<arg>-Amicronaut.processing.group=mn.maven.jspecify</arg>
<arg>-Amicronaut.processing.module=mn-maven-jspecify</arg>
</compilerArgs>
<annotationProcessorPaths combine.children="append">
<path>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>${error-prone.version}</version>
</path>
<path>
<groupId>com.uber.nullaway</groupId>
<artifactId>nullaway</artifactId>
<version>${nullaway.version}</version>
</path>
<!-- Existing Micronaut processors -->
...
..
.
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
The above example adds two annotation processors. Error Prone and NullAway.
The previous code sample configures NullAway:
-XepOpt:NullAway:AnnotatedPackagessets the list of packages that should be considered properly annotated according to the NullAway convention-Xep:NullAway:ERRORsets the NullAway check severity to ERROR, which should cause compilation to fail whenNullAwayviolations are found.
As described in the error prone Maven documentation, I added a .mvn/jvmconfig file with the following content:
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
A failure looks like:
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.14.1:compile (default-compile) on project mn-maven-jspecify: Compilation failure
[ERROR] /Users/sdelamo/github/sdelamo/micronaut-maven-nullaway-demo/src/main/java/example/micronaut/GreetingController.java:[21,39] [NullAway] dereferenced expression greetingService.greet() is @Nullable
[ERROR] (see http://t.uber.com/nullaway )
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
Tags: #micronaut #nullaway #jspecify