Files
javademos/docs/15-build-files.md

4.6 KiB

15. Build files: what the 21 to 25 upgrade does to Maven, Gradle and Lombok

Prev: 14. The 21 to 25 lane · Back to the README

The build half of the upgrade guide, run by scripts/upgrade.sh. Everything below is a run on JDK 21, 23, 24, 25, 26 or 27; the transcripts are in output/92 to output/97.

15.1 Maven: maven.compiler.release

lanes/upgrade/pom.xml sets maven.compiler.source, target and release to 25.

  • Building on JDK 21 fails at once: error: release version 25 not supported. The build JDK has to be 25 before the release can be 25 (92).
  • Building on JDK 25 succeeds and the class file has major version 69. Building on JDK 27 with release still 25 also succeeds and also produces version 69: a newer JDK can compile for 25. Running that class on JDK 21 gives UnsupportedClassVersionError (65 is Java 21).
  • --release N restricts the compiler to the API of release N, not just the language level and byte-code version. 94: JDK 27's javac --release 25 rejects String.equalsFoldCase (new in 26). javac -source 25 -target 25 compiles it with a warning, and the class then dies on a JDK 25 with NoSuchMethodError. That, and not "rejecting internal APIs", is what --release buys you.

15.2 Gradle: the toolchain block, and the JDK that runs Gradle

lanes/upgrade/build.gradle.kts asks for a Java 25 toolchain. Two different JDKs are involved (93):

  • the toolchain JDK, which compiles your code: Gradle 8.14.3 found and used JDK 25 for this while itself running on JDK 21;
  • the JDK that runs Gradle itself: Gradle 8.14.3 on JDK 25 fails (25.0.4.1 with a Kotlin DSL script; Unsupported class file major version 69 with a Groovy one, seen in a side run), Gradle 9.1.0 on JDK 25 works.

Gradle's compatibility table, read 2026-09-21: Java 25 needs Gradle 9.1.0, Java 26 needs 9.4.0, and Java 27 is not listed for any release yet.

15.3 Lombok and annotation processing

lanes/src/LombokProbe.java needs Lombok's processor to compile. 95:

Lombok JDK 25 JDK 26 JDK 27
1.18.34 fails: ExceptionInInitializerError fails fails
1.18.40 compiles compiles fails
1.18.46 compiles compiles fails
1.18.48 compiles compiles compiles

The probe uses one annotation, so "compiles" is a lower bound: the Lombok changelog is the support statement (JDK 25 in 1.18.40, JDK 26 in 1.18.46, JDK 27 in 1.18.48). 1.18.40 compiling on 26 does not contradict it.

Separate trap, same file: since JDK 23 javac no longer runs annotation processors it merely finds on the class path. JDK 21 compiles the probe with a Note: Annotation processing is enabled...; JDK 23, 24 and 25 give cannot find symbol for getName(). Maven and Gradle projects that declare the processor with annotationProcessorPaths / annotationProcessor are unaffected; hand-run javac lines and IDE setups that relied on discovery are not.

15.4 Mockito and ByteBuddy

lanes/src/MockitoProbe.java mocks an interface and stubs one call. 97, each Mockito with the ByteBuddy it declares:

Mockito ByteBuddy JDK 21 JDK 25 JDK 27
5.12.0 1.14.15 works fails fails
5.17.0 1.15.11 works fails fails
5.18.0 1.17.5 works works works
5.23.0 1.17.7 works works works

The failure is Mockito cannot mock this class (ByteBuddy cannot instrument class files newer than it knows). 5.18.0 was the first version that worked; 5.14.2, 5.16.1 and 5.17.0 failed in a side run (not committed). Even on the working versions the JVM prints WARNING: A Java agent has been loaded dynamically on 21, 25 and 27, and Mockito 5.23.0 adds a note that self-attaching will stop working: the fix is to add Mockito as an agent, as its documentation describes.

15.5 jdeps --jdk-internals

96 is jdeps --jdk-internals on the class from UnsafeWarning.java: it names sun.misc.Unsafe before the JVM prints its run-time warning (86).

Prev: 14. The 21 to 25 lane · Back to the README