Migration to Java 25
Please add any missing hints to this document!
See TC's approved Officially Supported Technologies document for the target release:
Help
Ask on the #development Slack channel if a module doesn't build on Java 25.
GitHub Actions
Most modules use the centralized maven.yml workflow (README-maven.md). Go to theuses: folio-org/.github/.github/workflows/maven.yml@v1
entry and add
with:
java-version: '25'to switch the java version, and you are done!
Delete
docker-health-command: 'wget --no-verbose --tries=1 --spider http://localhost:8081/admin/health || exit 1'because the Docker Hardened Images (to be approved by TC) don’t ship with wget for security reasons. Instead add an integration test for the mvn verify phase that tests the container. Examples: mod-permissions PermsIT, mod-oai-pmh ModTenantApiIt, mod-inventory InventoryIT.
The following applies only to modules that use their own GitHub Action Workflows:
All modules build via GitHub Actions. Change java-version from '21' to '25' in every .github/workflows/*.yml file:
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '25'
cache: mavenAlso update the Maven dependency cache key to avoid cache collisions between JDK 21 and JDK 25 build runs:
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-jdk25-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-jdk25-maven-Dockerfile
Umbrellaleaf | Policy: Unspecified Versions suggests to use Docker Hardened Images (dhi.io/eclipse-temurin:25-alpine), however, TC hasn’t approved it yet. Use this Dockerfile, adjust the port number as needed:
FROM docker.io/folioci/eclipse-temurin:25-alpine
WORKDIR /app
COPY ./target/*.jar app.jar
EXPOSE 8081
CMD ["java", "-jar", "app.jar"]If there are multiple .jar files in the target directory and we only need the -fat.jar file use this Dockerfile, adjust the port number as needed:
FROM docker.io/folioci/eclipse-temurin:25-alpine
WORKDIR /app
COPY ./target/*-fat.jar app.jar
EXPOSE 8081
CMD ["java", "-jar", "app.jar"]You should use a .dockerignore file, for examples see https://github.com/folio-org/folio-tools/tree/master/folio-java-docker/openjdk21#sample-dockerignore-file
If not using Docker Hardened Images (to be confirmed by TC):
Change the base image from eclipse-temurin:21 to eclipse-temurin:25:
# Before
FROM folioci/alpine-jre-openjdk21:latest
# After
FROM eclipse-temurin:25-jre-alpineFor details see the folio-java-docker repository.
Note on Alpine: If a module uses native libraries that require glibc, use a multi-stage build and install gcompat. If it doesn’t work: Before switching to
eclipse-temurin:25-jre(Debian slim) consult FOLIO Security Team.
POM Files
In the <properties> block change the java.version to 25:
<java.version>25</java.version>In the <properties> block:
delete the
<maven.compiler.source>entrydelete the
<maven.compiler.target>entry
Also ensure the maven-compiler-plugin
has version 3.13.0 or later
uses
<release>in the<configuration>sectiondoes not use
<source>in the<configuration>sectiondoes not use
<target>in the<configuration>section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.16.0</version>
<configuration>
<release>${java.version}</release>
</configuration>
</plugin>If using lombok also add <annotationProcessorPaths> to maven-compiler-plugin configuration, otherwise you get “incompatible types: … cannot be converted to lombok.val” or “variable … not initialized in the default constructor” compile error:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.16.0</version>
<configuration>
<release>${java.version}</release>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>If using jacoco-maven-plugin bump to version 0.8.15 or later.
Upgrade the Maven Wrapper to Maven 3.9.8+:
mvn wrapper:wrapper -Dmaven=3.9.8Breaking Changes
SecurityManager removed (JEP 486)
java.lang.SecurityManager is permanently disabled in JDK 24+. All related APIs throw UnsupportedOperationException at runtime.
Search your module for any direct usage:
grep -r "SecurityManager\|AccessController\|doPrivileged" src/Most modules are unaffected — the risk is in transitive dependencies using old framework versions. Upgrading Spring Boot to 3.5.x (see below) resolves the most common cases.
If you find AccessController.doPrivileged() in your own code, replace it with a direct call — the privileged block pattern has no effect without a SecurityManager.
Non-generational ZGC removed (JEP 490)
The JVM flag -XX:-ZGenerational no longer exists in JDK 24+. Any JAVA_OPTS containing it will cause JVM startup failure.
Check your module's environment configuration:
grep -r "ZGenerational" .Remove the flag wherever found. The safe replacement for JDK 25:
JAVA_OPTS="-XX:+UseZGC -Xmx512m"Note: Helm charts and Kubernetes
values.yamlfiles are handled by the DevOps team — raise a ticket if you find these in your module's infrastructure files.
sun.misc.Unsafe warnings (JEP 498)
Direct use of sun.misc.Unsafe memory-access methods now emits runtime warnings and is targeted for removal in a future JDK. This is most commonly introduced via older library versions.
The dependency upgrades below (especially Spring Boot 3.5.x and Netty 4.1.100+) resolve the majority of these warnings automatically.
Spring Boot
folio-spring-support is already on Spring Boot 4.1.1 (Spring Framework 7.x). Modules that inherit from it only need the Java version bump — no Spring Boot upgrade is required for those modules.
Check which category your module falls into:
# Does your module depend on folio-spring-support?
mvn dependency:tree | grep folio-spring-supportModules using folio-spring-support (majority of mod-* modules)
Spring Boot 4.1.1 is already JDK 25-compatible. The only required change is bumping java.version to 25 in your pom.xml (covered in the POM Files section above).
No Spring Boot upgrade needed. Verify you are on folio-spring-support 10.x or later:
<dependency>
<groupId>org.folio</groupId>
<artifactId>folio-spring-base</artifactId>
<version>10.1.0</version> <!-- or latest 10.x -->
</dependency>Notable Spring Boot 4.x items to be aware of
The Spring Boot 4.0 Migration Guide describes breaking changes from 3.x. If your module was previously migrated from Spring Boot 3.x to 4.x as part of folio-spring-support adoption, these should already be resolved. Double-check:
@MockBean→@MockitoBean,@SpyBean→@MockitoSpyBeanin all tests.management.endpoints.access.defaultreplacesmanagement.endpoints.enabled-by-default(Boolean →none/read-only/unrestricted).Virtual threads are enabled by default in Spring Boot 4.x for Tomcat/Jetty. Review any code that assumes platform-thread identity or uses
ThreadLocalfor per-request state.spring-cloud-starter-openfeign— use the Spring Boot 4.x-compatible release. Check the latest tag at https://github.com/spring-cloud/spring-cloud-openfeign/tags.
Modules NOT using folio-spring-support (standalone Spring Boot modules)
If your module manages its own Spring Boot parent independently, upgrade spring-boot-starter-parent to at least 3.5.x, or align with the platform by upgrading to 4.1.x. Find the latest tags at https://github.com/spring-projects/spring-boot/tags.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version> <!-- recommended: align with folio-spring-support -->
</parent>Consult the Spring Boot 3.5 Release Notes and Spring Boot 4.0 Migration Guide for the full set of changes if jumping from 3.x to 4.x.
Lombok
See POM Files section above.
Mockito
Mockito 5.x is required for JDK 25 compatibility. It pulls in ByteBuddy 1.14+ which removes direct sun.misc.Unsafe usage.
<properties>
<argLine></argLine>
<mockito.version>5.16.0</mockito.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
<configuration>
<argLine>
@{argLine}
-javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar
-Xshare:off
</argLine>
</configuration>
</plugin>
</plugins>
</build>See the Mockito Javadoc for inline-mock-maker instrumentation details if tests fail.
Jackson
Remove jackson-databind-afterburner if declared — it uses sun.misc.Unsafe and is incompatible. Replace with jackson-module-blackbird or remove entirely (the standard Jackson serialiser is sufficient for most FOLIO use cases):
<!-- Remove this -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-afterburner</artifactId>
</dependency>
<!-- Add this if needed for performance -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-blackbird</artifactId>
</dependency>Upgrade jackson-databind to 2.17.0 or later.
Vert.x based modules
Modules using Vert.x fall into two groups depending on which FOLIO library they use. In both cases the underlying Vert.x runtime is already on 5.x — only the Java version bump is required.
Check which library your module uses:
mvn dependency:tree -Dincludes=org.folio:vertx-lib,org.folio:domain-models-runtimefolio-vertx-lib based modules
folio-vertx-lib is currently on Vert.x 5.1.5 and Java 21 (version 4.2.0-SNAPSHOT). It already has maven-jdeps-plugin configured in the build.
For modules depending on folio-vertx-lib, only the Java version bump is needed:
<!-- In maven-compiler-plugin configuration -->
<release>25</release>Upgrade to the latest folio-vertx-lib 4.x release to ensure you are on Vert.x 5.1.x:
<dependency>
<groupId>org.folio</groupId>
<artifactId>folio-vertx-lib</artifactId>
<version>4.x.x</version> <!-- use latest released version -->
</dependency>Note: If your module is still on Vert.x 4.x directly (not via
folio-vertx-lib), you must upgrade to Vert.x 5.x first. Vert.x 5 is a major version with breaking API changes — see the Vert.x 5 migration guide before proceeding.
Native transport and JNI warnings (JEP 472)
Vert.x modules that use native transports (epoll, io_uring on Linux) will emit JNI restriction warnings on JDK 24+ because Netty loads native libraries via System.loadLibrary(). Add --enable-native-access=ALL-UNNAMED to JAVA_OPTS to suppress:
JAVA_OPTS="-XX:+UseZGC -Xmx512m --enable-native-access=ALL-UNNAMED"Or remove the native transport dependency if your module does not require it for performance — for most FOLIO edge modules the NIO transport is sufficient.
Event loop model vs virtual threads
Vert.x 5.x supports virtual threads via explicit opt-in (VertxOptions configuration). The event-loop model remains the default and is unaffected by JDK 25. Do not enable virtual threads unless the module has been tested with them.
Hazelcast clustering (if used)
If a module uses vertx-hazelcast for cluster management, verify Hazelcast is on 5.x:
mvn dependency:tree | grep hazelcastvertx-hazelcast >= 5.0.0 automatically ships with Hazelcast 5: https://github.com/vert-x3/vertx-hazelcast/commit/ed36d7598ade718efa60b1035e051f9c206bd982
Most FOLIO Vert.x modules do not use clustering — skip if vertx-hazelcast is not in the dependency tree.
RMB (raml-module-builder) based modules
RMB is currently on Vert.x 5.1.6 and Java 21 (version 36.1.0-SNAPSHOT).
For modules depending on RMB, only the Java version bump is required — RMB already manages Vert.x 5.x and all compatible dependencies transitively.
Upgrade to the latest RMB 36.x release:
<dependency>
<groupId>org.folio</groupId>
<artifactId>domain-models-runtime</artifactId>
<version>36.x.x</version> <!-- use latest released version -->
</dependency>Then bump the compiler target in your module's pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>25</release>
</configuration>
</plugin>RMB-specific notes:
RMB uses AspectJ (
1.9.22.1) for AOP. Theaspectj-maven-pluginandaspectjrtare compatible with JDK 25 at this version — no changes needed.RMB still uses
javax.ws.rs-api(oldjavaxnamespace, notjakarta). This is a known RMB characteristic and is unrelated to the JDK 25 upgrade — do not attempt to migrate tojakarta.ws.rsas part of this work.RMB uses Micrometer (
1.17.0) for metrics. This version is compatible with JDK 25.The JNI / native transport note above applies equally to RMB-based modules.
Verify RMB upgrade with:
mvn clean verify -Pintegration-test--add-opens flags
Review any --add-opens or --add-exports flags in your pom.xml, build.gradle, or test runner configuration. Some of these were added as workarounds for earlier JDK versions and may no longer be required. Remove any that are obsolete; document any that must remain.
Migrated repositories
FOLIO-4565 – Epic: Upgrade to Java 25 and Spring Boot 3.5
Update this section with proof-of-concept migration PRs as they are completed.
Repository | PR / branch | Build result |
|---|---|---|
|
|
|
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
success | ||
|
|
|
Dependency version targets
Library | Current version in FOLIO | Action for JDK 25 | Notes |
|---|---|---|---|
folio-spring-support | 10.1.0-SNAPSHOT (Spring Boot 4.1.1) | Bump | Spring Boot 4.1.1 is JDK 25-compatible |
folio-vertx-lib | 4.2.0-SNAPSHOT (Vert.x 5.1.5) | Bump compiler | Already on Vert.x 5.x |
raml-module-builder (RMB) | 36.1.0-SNAPSHOT (Vert.x 5.1.6) | Bump compiler | Already on Vert.x 5.x; uses javax (not jakarta) |
Spring Boot (standalone modules) | — | 4.1.1 recommended, 3.5.x minimum | Align with folio-spring-support if possible |
Vert.x (direct dependency) | — | 5.1.x | 4.x is incompatible — upgrade to 5.x first |
spring-cloud-starter-openfeign | — | Spring Boot 4.x-compatible release | Check https://github.com/spring-cloud/spring-cloud-openfeign/tags |
Mockito | 5.23.0 (in folio-vertx-lib / RMB) | Already compatible | folio-spring-support modules: use 5.16.0+ |
Jackson Databind | — | 2.17.0+ |
|
Jackson Afterburner | — | Remove | Replace with blackbird or remove |
Testcontainers | 2.0.4 (in folio-vertx-lib / RMB); 2.0.5 (in Spring Boot 4.1) | Already compatible |
|
Liquibase | — | 4.27.0+ | Spring Boot modules only |
Flyway | — | 10.10.0+ | Spring Boot modules only |
Bouncy Castle | — | 1.78+ |
|
Hazelcast (Vert.x clustering only) | — | 5.x | Check with |