Keep your Maven build cache across Java 21 and Java 27
In our test, a Maven build cached on Java 21 was restored on Java 27 and back, so switching Java restored the cached build instead of rebuilding. At FosterStack we prove everything, so here is how we proved it to ourselves: three real series of builds, both directions, and what the restored jar contains.
The short answer
- In our test, a build cached on one Java was restored on the other, in both directions. Nothing was rebuilt.
- The jar you get is the one the first build made. The manifest inside it names that Java:
Build-Jdk-Spec: 21orBuild-Jdk-Spec: 27. - Gradle behaved differently in our other run: a different Java gave separate entries there.
What we ran
A one-module Maven project, built five times in each of three series, with mvn verify and JAVA_HOME set for each build. For series B and C we saved mvn -v for every build to confirm which Java ran (series A has only its summary): Temurin 21.0.12.1 or OpenJDK 27 from Homebrew.
- Series A:
maven.compiler.releaseset to 17, first build on Java 21. - Series B: no
maven.compiler.release, first build on Java 21. - Series C: no
maven.compiler.release, first build on Java 27.
Every build used a fresh copy of the project and an empty local build cache. All builds ran on the same Mac, shared one already-filled local Maven repository (offline), and shared the cache server. Each series started on an empty server. Every build ended with BUILD SUCCESS.
| Build | Series B: Java | Series B: result | Series C: Java | Series C: result |
|---|---|---|---|---|
| 1 | 21 | Built | 27 | Built |
| 2 | 21 | Restored | 27 | Restored |
| 3 | 27 | Restored | 21 | Restored |
| 4 | 27 | Restored | 21 | Restored |
| 5 | 21 | Restored | 27 | Restored |
"Restored" means the log said Found cached build, restoring and Maven skipped the compile and test steps. Series A, with the release setting, gave the same pattern as series B.
Which jar you get
In series B all five jars had one checksum, 0f4e29a2..., so the jar restored on Java 27 was the jar built on Java 21. In series C all five had another, ddb48271..., the jar built on Java 27. The files inside the two jars differ in a single place. The manifest in the first says Build-Jdk-Spec: 21 and in the second Build-Jdk-Spec: 27. The compiled class inside was byte-for-byte the same in both (we compared it with cmp). Without the release setting the compiler plugin still compiled for Java 8 on both Javas (the log said target 1.8), which is why the class was the same.
What this means
- Switching Java still restored from the cache. In our runs the extension restored the existing result and did not rebuild on the new Java.
- In our runs, the first build decided what everyone got. If a Java 21 build fills the cache, a Java 27 build restores a jar that says it was built on Java 21. Here that was harmless, because the class was identical. We did not test a project whose output depends on the Java that compiles it.
- For Gradle it was the other way round. Our Gradle run on a different Java ran its tasks again and made its own entries; see Gradle build cache hit rate low, cause 3.
Our advice, not a test result: use one Java version for the builds that fill a shared cache, so you know which Java made what you restore.
We did not test other Java vendors, other Maven or extension versions, a project whose build settings change with the Java version (for example, profiles that switch on by JDK), or Linux or Windows.
If you do not see restores
See Maven build cache not restoring. The setup is on Maven remote build cache setup, and the multi-module case is on Maven build cache extension in a multi-module project.
Run on Maven 3.10.0 with maven-build-cache-extension 1.2.3 and FosterStack Cache 0.2.1, macOS (arm64), Temurin 21.0.12.1 and OpenJDK 27 (Homebrew).