Java 27 and 26: runnable demos and captured output for every JEP, plus version lanes
Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# 15. Build files: what the 21 to 25 upgrade does to Maven, Gradle and Lombok
|
||||
|
||||
Prev: [14. The 21 to 25 lane](14-lanes-21-to-25.md) · [Back to the README](../README.md)
|
||||
|
||||
The build half of the [upgrade guide](https://ankurm.com/java-21-to-25-upgrade-guide-ai-prompts/), 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`](../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](output/92-maven-release.txt)).
|
||||
* 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](output/94-release-flag.txt): 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`](../lanes/upgrade/build.gradle.kts) asks for a Java 25 toolchain. Two different JDKs are involved ([93](output/93-gradle-toolchain.txt)):
|
||||
|
||||
* 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](https://docs.gradle.org/current/userguide/compatibility.html), 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`](../lanes/src/LombokProbe.java) needs Lombok's processor to compile. [95](output/95-lombok-matrix.txt):
|
||||
|
||||
| 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](https://projectlombok.org/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`](../lanes/src/MockitoProbe.java) mocks an interface and stubs one call. [97](output/97-mockito-bytebuddy.txt), 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](output/96-jdeps-internals.txt) is `jdeps --jdk-internals` on the class from [`UnsafeWarning.java`](../lanes/src/UnsafeWarning.java): it names `sun.misc.Unsafe` before the JVM prints its run-time warning ([86](output/86-unsafe-and-jni.txt)).
|
||||
|
||||
Prev: [14. The 21 to 25 lane](14-lanes-21-to-25.md) · [Back to the README](../README.md)
|
||||
Reference in New Issue
Block a user