DEV Community

Query Filter
Query Filter

Posted on

desktop-1

Java 21 Migration Assessment Summary

This document provides a consolidated, artifact-by-artifact compatibility analysis for migrating application dependencies to Java 21 LTS, Spring Framework 6.1+/7.0+, and the Jakarta EE 10/11 ecosystem.

1. Logging Frameworks & Utilities

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
log4j:log4j
${log4j.version} (Analyzed: 1.2.17)
Java 1.1 – 1.4 (Major 45.3 / 48.0) Legacy logging framework for application logging, category hierarchies, and log appenders. Not Ready / Incompatible
End-of-life; broken by SecurityManager removal, severe JPMS illegal reflection, and lack of module-info. Must migrate to log4j-1.2-api (2.23.1+) bridge and log4j-core.
org.apache.logging.log4j:log4j-core
${log4j.version} (Analyzed: < 2.21.0 vs 2.23.1+)
Java 8 (Major 52.0) Core logging engine and appender implementation for Log4j 2. Conditional / Version-Dependent
Versions < 2.21.0 fail or warn on JPMS encapsulation and cause Virtual Thread carrier pinning. Enforce version 2.23.1+ for full compatibility.
org.slf4j:slf4j-api
2.0.13
Java 8 (Major 52.0) Standard logging facade abstraction for Java applications. Java 21 Ready
Fully compatible. Uses Java ServiceLoader (JPMS-compliant), supports Virtual Threads natively, and integrates with Spring 6+/7+ and Jakarta EE 10+.

2. Messaging, Middleware & Distributed Systems

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
com.citi.150667.quantum3_7:UMSJMS_5.3.1_Linux-glibc-2.5-x86_64_jdk1.5.0_12
3.7_A4
Java 5 (JDK 1.5.0_12 / Major 49.0) Proprietary Ultra Messaging (Informatica/LBM) JMS client library wrapper. Not Ready / Incompatible (High Risk)
Locked to legacy glibc 2.5 JNI C-libraries (libLBM.so), uses legacy javax.jms.* namespace, and causes Virtual Thread pinning. Must replace or upgrade to Informatica UM 6.x+.
jgroups:jgroups-all
2.4.1
Java 1.4 / 5 (Major 48.0 / 49.0) Legacy uber-jar for cluster group communication and node membership management. Not Ready / Obsolete
Uses removed thread suspension APIs (Thread.stop()), obsolete Maven coordinates, and insecure Java serialization. Must migrate to org.jgroups:jgroups (5.x+).
com.solacesystems:sol-jms
10.6.0
Java 8 (Major 52.0) JMS client library for Solace PubSub+ event brokers. Incompatible / Upgrade Required
Uses legacy javax.jms.* namespace, lacks JDK 17/21 modularity updates, and carries Virtual Thread pinning risks. Upgrade to sol-jms-jakarta (10.30.1+) or sol-jms (10.30.2+).
com.ibm:com.ibm.mq.jmqi
7.5.0.6
Java 6 / 7 (Major 50.0 / 51.0) IBM WebSphere MQ Java Message Queue Interface client driver. Incompatible / Critical Upgrade Required
Fails on JPMS strong encapsulation, uses legacy javax.jms.* namespace, lacks TLS 1.3 support, and pins carrier threads. Upgrade to com.ibm.mq.jakarta.client (9.3.0+ / 9.4.x).
com.gemstone.gemfire:gemfire
8.2.7
Java 7 / 8 (Major 51.0 / 52.0) Distributed in-memory data grid and caching platform. Incompatible / Critical Migration Required
Deep reliance on removed/blocked internal APIs (sun.misc.Unsafe, sun.nio.ch.DirectBuffer), obsolete SecurityManager, and heavy carrier thread pinning. Migrate to Tanzu GemFire 10.x / Apache Geode 1.15+.
com.tibco:tibjms
${tibco.version} (Analyzed: 8.2.21 / 8.2.x)
Java 7 / 8 (Major 51.0 / 52.0) TIBCO Enterprise Message Service (EMS) JMS client driver. Conditional / Vendor Certification Required
Uncertified for Java 21 by vendor; uses javax.jms.* namespace (incompatible with Spring 7 / Jakarta EE 10+). Test on Java 21 and plan upgrade to EMS 10.x.
com.tibco:tibcrypt
${tibco.version} (Analyzed: 8.2.21 / 8.2.x)
Java 7 / 8 (Major 51.0 / 52.0) Cryptographic and TLS provider library for TIBCO EMS clients. Conditional (High Risk)
Legacy security settings (weak ciphers, old TLS versions) are blocked by modern JDK defaults (jdk.tls.disabledAlgorithms). Upgrade server/client TLS or apply security property overrides.
com.tibco:tibjmsadmin
${tibco.version} (Analyzed: 8.2.21 / 8.2.x)
Java 7 / 8 (Major 51.0 / 52.0) Administration and management API for TIBCO EMS servers. Conditional / Follows tibjms
Inherits tibjms compatibility and TLS limits. Recommend removing from runtime web application classpaths if administration functionality is unused.
com.tibco:tibrvj
${tibco.version} (Analyzed: 8.2.21)
Java 7 / 8 (Major 51.0 / 52.0) Java wrapper API for TIBCO Rendezvous messaging protocol. Conditional (Highest Risk)
Relies on native JNI C-libraries (System.loadLibrary). Requires 64-bit native binaries compiled for the OS and JDK 21 environment. Event loops pin Virtual Threads.

3. Spring Framework Baseline (Core, Web, Data)

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
org.springframework:spring-core
${spring.version} (Analyzed: 5.x vs 6.1.x+)
Java 8 (Major 52.0) for 5.x Core framework utilities, ASM bytecode parsing, and dependency injection foundation. Conditional / Major Upgrade Required
Spring 5.x fails with IllegalArgumentException on Java 21 class files (Major 65.0) and uses removed SecurityManager. Upgrade to Spring 6.1.x+ / 7.0.x (Boot 3.2+).
org.springframework:spring-beans
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Bean factory, lifecycle management, and property injection container. Java 21 Ready
Fully compatible on 7.0.9 baseline. Supports Java 21 class file parsing, Virtual Thread safe singleton registries, and native record component auto-wiring.
org.springframework:spring-context
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Application context, component scanning, event publishing, and scheduling interface. Java 21 Ready
Fully compatible on 7.0.9 baseline. Native Virtual Thread task executor support for @async/events, no SecurityManager calls, and full Java Record scanning support.
org.springframework:spring-context-support
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Integrations for third-party libraries (Quartz, Caffeine caching, JavaMail). Java 21 Ready
Fully compatible on 7.0.9 baseline. Virtual Thread aware Quartz/Mail execution and modern Jakarta/JPMS cache abstractions.
org.springframework:spring-web
${spring.version} (Target: 7.0.0 / 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Core web HTTP abstractions, REST clients, and web utilities. Conditional / Version-Dependent
Spring 5.3.x/6.0.x fail due to ASM limits and javax.servlet namespace. Spring 6.1+ / 7.0.x is fully compatible with Virtual Threads and Jakarta EE 10/11.
org.springframework:spring-webmvc
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Model-View-Controller framework for HTTP web applications and REST APIs. Java 21 Ready (on 7.0.9)
Fully compatible on 7.0.9 baseline. Requires Servlet 6.1 container (Tomcat 11 / Jetty 12.1), jakarta.servlet namespace, and Virtual Thread request handling.
org.springframework:spring-jdbc
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x JDBC abstraction framework, JdbcTemplate, and JdbcClient. Java 21 Ready (on 7.0.9)
Fully compatible on 7.0.9 baseline. Requires Java 21-compliant JDBC drivers and HikariCP connection pool to avoid Virtual Thread carrier pinning.
org.springframework:spring-tx
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Transaction management framework (@Transactional, TransactionTemplate). Java 21 Ready (on 7.0.9)
Fully compatible on 7.0.9 baseline. Fully supports jakarta.transaction.* namespace and thread-bound virtual thread transaction contexts.
org.springframework:spring-expression
${spring.version} (Target: 7.0.9)
Java 17+ (Major 61.0+) for 6.x/7.x Spring Expression Language (SpEL) evaluation engine. Java 21 Ready
Fully compatible on 7.0.9 baseline. Expressions attempting reflective access to java.base internal classes fail under JPMS encapsulation. Requires -parameters compiler flag.

4. Web Containers & Application Servers

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
org.apache.tomcat.embed:tomcat-embed-core
10.1.28
Java 11 (Major 55.0) Core embedded Tomcat HTTP engine and Servlet container. Java 21 Ready
Fully compatible. Implements Jakarta Servlet 6.0 (jakarta.servlet.*), natively supports Virtual Threads without carrier pinning, and compliant with JPMS encapsulation.
org.apache.tomcat.embed:tomcat-embed-jasper
10.1.28
Java 11 (Major 55.0) Embedded JSP compiler and execution engine for Tomcat. Conditional / Patch Upgrade Required
Runs on Java 21, but 10.1.28 has known security CVEs in JSP compilation and requires an updated Eclipse JDT compiler (ECJ 3.35+) for Java 21 syntax. Upgrade to 10.1.34+.

5. Web, XML, Mail & Serialization Standards

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
com.sun.mail:jakarta.mail
1.6.7
Java 8 (Major 52.0) JavaMail API and reference implementation for SMTP/IMAP protocol handling. Conditional / Upgrade Required
Transitional release that retains legacy javax.mail. namespace despite artifact name. Must upgrade to Jakarta Mail 2.x+ / Eclipse Angus (jakarta.mail.) for Spring 6+/7+.
javax.xml.bind:jaxb-api
2.3.1
Java 8 / 11 (Major 52.0) Java Architecture for XML Binding API specification interfaces. Not Ready / Migration Required
Uses legacy javax.xml.bind.* namespace. Modern stacks fail with ClassNotFoundException. Upgrade to jakarta.xml.bind:jakarta.xml.bind-api (3.x / 4.x).
org.glassfish.jaxb:jaxb-runtime
2.3.9
Java 8 (Major 52.0) Reference implementation engine for JAXB XML marshalling and unmarshalling. Conditional / Upgrade Required
Binds to javax.xml.bind namespace. Dynamic byte-injection uses sun.misc.Unsafe, triggering JPMS warnings/degradation. Upgrade to org.glassfish.jaxb:jaxb-runtime:4.x.
com.fasterxml.jackson.core:jackson-databind
${jackson.version} (Analyzed: < 2.15.0 vs 2.17.2+)
Java 8 (Major 52.0) Core JSON data-binding, object mapper, and serialization library. Conditional / Version-Dependent
Versions < 2.15.0 fail/warn on private record inspection and JPMS reflection. Enforce version 2.17.2+ for full Java Record patterns and Loom compatibility.
com.fasterxml.jackson.core:jackson-annotations
${jackson-annotations.version} (Target: 2.17.2)
Java 8 (Major 52.0) General annotations for Jackson JSON serialization and deserialization. Java 21 Ready
Fully compatible. Jackson 2.17.2 annotations natively inspect Java 21 Record components, @JsonCreator modes, and compact record constructors.

6. Database Drivers

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
com.oracle.database.jdbc:ojdbc11
${ojdbc.version} (Target: 23.5.0.24.07)
Java 11 (Major 55.0) Oracle Database JDBC Type 4 Network Driver. Java 21 Ready
Fully compatible on Oracle 23c (23.5.0.24.07) baseline. Socket I/O and network handlers are refactored to prevent Virtual Thread carrier pinning.

7. Testing & Benchmarking Frameworks

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
junit:junit
${junit.version} (Analyzed: 4.12 vs 4.13.2)
Java 5 / 6 (Major 49.0 / 50.0) Legacy JUnit 4 testing framework. Conditional / Migration Recommended
Versions < 4.13.2 fail under JPMS and have TemporaryFolder security flaws. Update to 4.13.2 for legacy bridging or migrate to JUnit Jupiter (JUnit 5.10.x+).
jfree:jfreechart
${jfreechart.version} (Analyzed: 1.0.13 vs 1.5.4)
Java 1.4 / 5 (Major 48.0 / 49.0) Server and desktop charting, graph rendering, and graphics library. Not Ready (Legacy Group ID)
Legacy 1.0.x versions depend on obsolete jcommon, use private AWT reflection, and lack JPMS modules. Must relocate to org.jfree:jfreechart:1.5.4+.
com.google.caliper:caliper
${caliper.version} (Analyzed: 0.5-rc1)
Java 6 / 7 (Major 50.0 / 51.0) Legacy Google Java microbenchmarking framework. Not Ready / Obsolete
Unmaintained; broken by removed sun.misc.Unsafe and SecurityManager. Cannot measure Virtual Threads. Migrate to OpenJDK JMH (org.openjdk.jmh:jmh-core:1.37).

8. Application Utilities, Templating, Scheduling & Internal Libraries

Artifact Name / Version Built Java Version Short Description Java 21 Readiness
com.citi.176352:qfix
1.3_C64
Java 11 (JDK 11.0.21 / Major 55.0) Internal financial FIX protocol engine/wrapper. Conditional / Unverified
Class files load, but behavior changes (JPMS encapsulation, removed APIs, default UTF-8 charset, Loom pinning) require verification via jdeps/jdeprscan and a dedicated test suite execution.
javax.transaction:jta
1.3
Java 7 / 8 (Major 51.0 / 52.0) Java Transaction API specification interfaces. Not Ready (Blocked by Namespace)
Loads on JVM, but Spring 7 / Jakarta EE 10+ ignores javax.transaction.. Replace with jakarta.transaction:jakarta.transaction-api:2.0.1+*.
javax.jms:javax.jms-api
2.0.1
Java 7 (Major 51.0) Java Message Service 2.0 API specification interfaces. Not Ready (Blocked by Namespace)
Loads on JVM, but incompatible with Spring 7 spring-jms (jakarta.jms.*). Retain only if constrained to legacy javax.jms providers (e.g., EMS 8.2.21).
org.quartz-scheduler:quartz
2.3.2
Java 7 (Major 51.0) Job scheduling framework for background and cron tasks. Conditional / Works with Caveats
Core runs, but uses HikariCP-java7 and javax.transaction in JobStoreCMT. Thread pool is not Virtual Thread aware. Upgrade to Quartz 2.5.x.
org.apache.velocity:velocity
1.7
Java 1.4 / 1.5 (Major 48.0 / 49.0) Legacy Velocity 1.x template rendering engine. Conditional / Not Recommended
End-of-life. Reflection-based introspection fails on JDK internal types under JPMS, commons-collections dependency issues, and thread pinning. Migrate to org.apache.velocity:velocity-engine-core:2.3+.

Artifact: log4j:log4j

Original JDK Target & Baseline Requirements

  • Group ID: log4j
  • Artifact ID: log4j
  • Version: ${log4j.version} (Analyzed baseline: Log4j 1.x legacy releases end-of-life since 2015; image specified version 1.2.17)
  • Compile-Time JDK Baseline: Java 1.1 - 1.4 (Major Version 45.3 / 48.0) for standard 1.2.x releases.
  • Java 21 Compatibility Status: Incompatible / Deprecated (Action Required: Complete Migration to Log4j 2.x or SLF4J Bridge).
    • Legacy Log4j 1.x Releases (log4j:log4j 1.2.17): Completely end-of-life and broken under Java 21 due to removed java.lang.SecurityManager invocations, severe JPMS illegal reflective access (sun.misc.Unsafe, java.lang.reflect), lack of module-info descriptors, and multiple unpatched critical remote code execution / deserialization security vulnerabilities (e.g., CVE-2019-17571, CVE-2021-4104).
    • Modern Replacement Baseline (Log4j 2.x Bridge or Core): Bridge legacy calls using org.apache.logging.log4j:log4j-1.2-api (2.23.1+) or replace entirely with org.apache.logging.log4j:log4j-core for full Java 21, JPMS, and Virtual Thread compatibility.

Key Architectural & Technical Characteristics on Java 21

1. SecurityManager Removal & JPMS Reflection Restrictions

  • Log4j 1.2.17 relies heavily on java.lang.SecurityManager and direct reflection into restricted system properties and private class members to configure appenders and resolve caller information.
  • Java 21 deprecates SecurityManager for removal and enforces strong encapsulation via JPMS, triggering InaccessibleObjectException or silent initialization failures when legacy Log4j 1.x appenders attempt internal access.

2. Virtual Threads Performance Pinning (Project Loom)

  • Log4j 1.x uses extensive coarse-grained synchronization across internal category nodes, hierarchy tree lookups, and appender output streams.
  • Under Java 21 Virtual Threads, executing synchronous file or network I/O inside these synchronized blocks causes severe carrier thread pinning, negating the throughput benefits of Project Loom.

3. Spring Framework 6.x / 7.0 & Jakarta EE Compatibility

  • Modern framework stacks (Spring Boot 3.x+, Spring 6.x / 7.0, Jakarta EE 10 / 11) have completely purged Log4j 1.x support.
  • Modern logging initializers require SLF4J 2.x bindings or native Log4j 2.x plugins, making log4j:log4j 1.2.17 unusable without bridge artifacts.

JVM Command-Line Flag Options (Legacy Workaround)

If temporarily forced to execute legacy Log4j 1.x binaries (1.2.17) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for legacy property lookup and reflective appenders
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Replace log4j:log4j with Log4j 2.x Bridge (log4j-1.2-api) and Core 2.23.1+

Remove log4j:log4j dependencies and replace them with the log4j-1.2-api bridge artifact alongside log4j-core to maintain full API compatibility while running safely on Java 21.

Maven Configuration

<properties>
    <!-- Set Log4j 2.x version to a Java 21 / Virtual Thread safe baseline -->
    <log4j.version>2.23.1</log4j.version>
</properties>

<!-- Replace legacy log4j:log4j with the Log4j 1.2 API Bridge and Log4j 2 Core -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-1.2-api</artifactId>
    <version>${log4j.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    log4jVersion = '2.23.1'
}

dependencies {
    // Replace legacy log4j:log4j with the Log4j 1.2 API Bridge and Log4j 2 Core
    implementation "org.apache.logging.log4j:log4j-1.2-api:${log4jVersion}"
    implementation "org.apache.logging.log4j:log4j-core:${log4jVersion}"
Enter fullscreen mode Exit fullscreen mode

Artifact: com.citi.150667.quantum3_7:UMSJMS_5.3.1_Linux-glibc-2.5-x86_64_jdk1.5.0_12:3.7_A4

Original JDK Target & Baseline Requirements

  • Group ID: com.citi.150667.quantum3_7
  • Artifact ID: UMSJMS_5.3.1_Linux-glibc-2.5-x86_64_jdk1.5.0_12
  • Version: 3.7_A4
  • Java Package Root: com.latencybusters.jms
  • Compile-Time JDK Baseline: Java 5 (JDK 1.5.0_12 / Major Version 49.0).
  • Java 21 Compatibility Status: Incompatible / High Risk.
    • JNI & Architecture Lock: Depends on native C binaries (libLBM.so / libLBMJNI.so) built against glibc 2.5 for 64-bit Linux environments. Operating this library on modern Linux kernels or containerized glibc implementations (e.g., glibc 2.31+) can lead to dynamic linker symbol mismatches or UnsatisfiedLinkError.
    • Namespace Migration Blocker: Employs the legacy javax.jms. namespace (ConnectionFactory, Destination, Session), which is incompatible with Spring Framework 7.0 and Jakarta EE 11 stacks requiring jakarta.jms..
    • JPMS Reflection Restrictions: Deep reflection on internal fields during initialization triggers InaccessibleObjectException under Java 21 unless explicitly opened via JVM options.

Key Architectural & Technical Characteristics on Java 21

  • JNI Native Layer & System Environment Dependencies:
    • Ultra Messaging (formerly Latency Busters Messaging / LBM) relies on JNI bindings to low-level socket and shared-memory native libraries (libLBM.so).
    • Running Java 21 on modern glibc versions (glibc 2.34+) with a legacy glibc 2.5 binary runtime can cause UnsatisfiedLinkError or SIGSEGV native crashes under high memory load.
  • Jakarta EE 11 / Spring 7.0 Namespace Collision (javax.jms.*):
    • The library implements javax.jms.ConnectionFactory, javax.jms.Destination, and javax.jms.MessageListener.
    • Spring 7.0 / Jakarta EE 11 requires jakarta.jms.*. Directly wiring UMS JMS 5.3.1 into Spring JmsTemplate or @JmsListener will fail at class-loading time (NoClassDefFoundError).
  • Virtual Threads Pinning Risk (Project Loom):
    • Synchronous message processing and JNI dispatch calls lock carrier threads during native C socket operations (LBM.poll()).
    • High-throughput messaging consumers using UMS JMS should execute on dedicated platform worker thread pools (Executors.newFixedThreadPool()) rather than Virtual Threads.

JVM Command-Line Flag Options (Temporary Workaround Only)

If forced to execute UMSJMS:5.3.1 on Java 21 during transitional phases, the following command-line flags and native library path configurations are required:

# Allow reflective access for legacy serialization/initialization
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED

# Point Java 21 to the native Ultra Messaging dynamic library directory
-Djava.library.path=/opt/informatica/um/5.3.1/lib
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Replace Legacy Proprietary Messaging Client

Because UMS JMS 5.3.1 targets JDK 1.5 and relies on glibc 2.5 JNI binaries, updating or wrapping this library is not viable for modern production deployments.

Maven Dependency Exclusion

<!-- REMOVE Legacy UMS JMS Dependency -->
<!--
<dependency>
    <groupId>com.citi.150667.quantum3_7</groupId>
    <artifactId>UMSJMS_5.3.1_Linux-glibc-2.5-x86_64_jdk1.5.0_12</artifactId>
    <version>3.7_A4</version>
</dependency>
-->
Enter fullscreen mode Exit fullscreen mode

Gradle Dependency Exclusion

// REMOVE Legacy UMS JMS Dependency
// implementation 'com.citi.150667.quantum3_7:UMSJMS_5.3.1_Linux-glibc-2.5-x86_64_jdk1.5.0_12:3.7_A4'
Enter fullscreen mode Exit fullscreen mode

Remediation & Migration Options

  • Upgrade Informatica UM Client: Update to an actively supported release of Informatica Ultra Messaging (UM 6.x+) compiled for Java 17/21 with modern 64-bit native libraries.
  • Apply Jakarta Transformer / Namespace Bridge: If an upgraded driver is unavailable, utilize the Eclipse Transformer tool at build time to translate javax.jms package calls into jakarta.jms calls, or migrate underlying transport protocols to modern enterprise brokers (e.g., Apache ActiveMQ Artemis, Solace, or Kafka).

Artifact: org.apache.logging.log4j:log4j-core

Original JDK Target & Baseline Requirements

  • Group ID: org.apache.logging.log4j
  • Artifact ID: log4j-core
  • Version: ${log4j.version} (Analyzed baseline: Log4j 2.x releases < 2.21.0)
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0) for 2.x releases.
  • Java 21 Compatibility Status: Conditional / Version-Dependent.
    • Legacy Log4j 2.x Releases (< 2.21.0): Fail or generate warnings under Java 21 due to removed SecurityManager calls, illegal reflective access to JDK internals (sun.misc.Unsafe), and legacy log4j-1.2-api bridge incompatibilities.
    • Modern Log4j 2.x Releases (>= 2.21.0 / 2.23.1+): Fully compatible with Java 21, JPMS module encapsulation, and Spring 7.0 / Jakarta EE 11 runtime environments.

Key Architectural & Technical Characteristics on Java 21

  • SecurityManager Removal & Reflection Restrictions (JPMS):
    • Older versions of Log4j 2.x rely on java.lang.SecurityManager to determine caller location and access restricted system properties.
    • Because Java 21 deprecates SecurityManager for removal (throwing UnsupportedOperationException if invoked under strict flags) and enforces strong encapsulation, legacy Log4j versions trigger InaccessibleObjectException when attempting internal reflection.
  • Virtual Threads Performance Optimization (Project Loom):
    • Log4j 2.x versions prior to 2.21.0 utilize heavy synchronized blocks inside appenders and context selectors, causing Virtual Thread Pinning on Java 21 carrier threads.
    • Log4j 2.21.0+ refactored internal locking mechanisms to use java.util.concurrent.locks.ReentrantLock and thread-local isolations, making it fully Virtual Thread safe.
  • Spring Framework 7.0 & Jakarta EE 11 Compatibility:
    • Modern Log4j 2.x integrates cleanly with Spring 7.0 via org.apache.logging.log4j:log4j-to-slf4j or native log4j-slf4j2-impl bindings.
    • If using web-based logging initializers (log4j-web), ensure versions >= 2.20.0 are used to support the jakarta.servlet.* namespace.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Log4j 2.x binaries (< 2.21.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal property lookup
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Log4j Version 2.23.1+

Ensure ${log4j.version} is defined as 2.23.1 (or latest 2.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Log4j version to a Java 21 / Virtual Thread safe baseline -->
    <log4j.version>2.23.1</log4j.version>
</properties>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    log4jVersion = '2.23.1'
}

dependencies {
    implementation "org.apache.logging.log4j:log4j-core:${log4jVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: jgroups:jgroups-all:2.4.1

Original JDK Target & Baseline Requirements

  • Group ID: jgroups
  • Artifact ID: jgroups-all
  • Version: 2.4.1
  • Compile-Time JDK Baseline: Java 1.4 / Java 5 (Major Version 48.0 / 49.0).
  • Java 21 Compatibility Status: Incompatible / High Risk (Obsolete).
    • JPMS Strong Encapsulation Breakage: Relies on legacy reflection, private field access, and obsolete thread suspension APIs (Thread.stop(), Thread.suspend()) that were permanently removed or blocked in modern JDKs.
    • Obsolete Maven Coordinates: The jgroups:jgroups-all uber-jar artifact path was abandoned over 15 years ago in favor of org.jgroups:jgroups.
    • Network & Serialization Risks: Uses legacy Java serialization without modern object filtering safeguards, exposing the application to Remote Code Execution (RCE) via untrusted cluster nodes.

Key Architectural & Technical Characteristics on Java 21

  • Deprecated Thread Management APIs:
    • JGroups 2.4.1 heavily uses legacy thread lifecycle control methods (java.lang.Thread.stop(), suspend(), resume()).
    • On Java 21, these methods throw UnsupportedOperationException or fail to compile, causing instant clustering thread failures at startup.
  • JPMS Encapsulation & Internal Reflection (sun.misc.*):
    • Uses internal sun.misc.Unsafe and deep reflection to serialize network packets and manage buffer allocation.
    • Under Java 21 strong encapsulation, these operations trigger InaccessibleObjectException unless explicit reflective access is opened via JVM flags.
  • Spring 7.0 & Modern Stack Incompatibility:
    • Modern distributed cache managers (e.g., Infinispan, Hazelcast, Redisson) and Spring 7.0 integration points cannot bind to JGroups 2.x APIs.

JVM Command-Line Flag Options (Temporary Workaround Only)

If forced to execute jgroups-all:2.4.1 on Java 21 during transitional phases, the following flags are required:

# Allow reflective access to core JDK structures
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Migrate to org.jgroups:jgroups (5.x+)

Replace the legacy 2.4.1 uber-jar with modern, actively maintained JGroups 5.x releases or a contemporary distributed clustering engine (e.g., Hazelcast / Redis).

Maven Configuration

<!-- REMOVE Legacy JGroups 2.4.1 Dependency -->
<!--
<dependency>
    <groupId>jgroups</groupId>
    <artifactId>jgroups-all</artifactId>
    <version>2.4.1</version>
</dependency>
-->

<!-- ADD Modern JGroups Dependency -->
<dependency>
    <groupId>org.jgroups</groupId>
    <artifactId>jgroups</artifactId>
    <version>5.3.8.Final</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

// REMOVE Legacy
// implementation 'jgroups:jgroups-all:2.4.1'

// ADD Modern JGroups
implementation 'org.jgro
Enter fullscreen mode Exit fullscreen mode

Artifact: com.sun.mail:jakarta.mail:1.6.7

Original JDK Target & Baseline Requirements

  • Group ID: com.sun.mail
  • Artifact ID: jakarta.mail
  • Version: 1.6.7
  • Package Namespace: Legacy javax.mail.*
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0).
  • Java 21 Compatibility Status: Conditional / Upgrade Required.
    • Namespace Blocker (javax vs jakarta): Version 1.6.7 is a transitional release that retained the legacy javax.mail. namespace despite the jakarta.mail artifact name. It is incompatible with Spring Framework 6+/7+ and Jakarta EE 9+ stacks requiring jakarta.mail..
    • JPMS Reflective Access: Uses reflection on dynamic JavaBeans DataHandler implementations and internal system properties during SMTP/IMAP protocol stream initialization, triggering InaccessibleObjectException under Java 21 unless opened.
    • Security & Maintenance: Obsolete release path superseded by Eclipse Angus / Jakarta Mail 2.x+.

Key Architectural & Technical Characteristics on Java 21

  • Jakarta EE Namespace Mismatch (javax.mail.*):
    • Although named jakarta.mail, version 1.6.7 still exposes class
    • es under javax.mail.*.
    • Modern frameworks (Spring Boot 3.x+, Spring Framework 6.0+, Jakarta EE 9+) fail to bind to javax.mail.Session or javax.mail.internet.MimeMessage, leading to NoClassDefFoundError or ClassNotFoundException.
  • JPMS Encapsulation & Dynamic JavaBeans Activation:
    • Jakarta Mail relies on the JavaBeans Activation Framework (jakarta.activation / javax.activation) to process MIME body parts and attachments.
    • Under Java 21 strong encapsulation, reflection on internal MIME providers triggers access warnings or failures unless the target packages are explicitly opened.
  • Virtual Threads Support (Project Loom):
    • Synchronous blocking socket I/O performed during SMTP/IMAP network transport (com.sun.mail.smtp.SMTPTransport) works under Virtual Threads, but legacy synchronization blocks inside MIME parsing may cause mild thread pinning during high-volume mail processing.

JVM Command-Line Flag Options (Temporary Workaround Only)

If forced to run com.sun.mail:jakarta.mail:1.6.7 on Java 21 during transition phases:

# Permit reflective access for dynamic JavaBeans Activation & system properties
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Artifact: javax.xml.bind:jaxb-api:2.3.1

Original JDK Target & Baseline Requirements

  • Group ID: javax.xml.bind
  • Artifact ID: jaxb-api
  • Version: 2.3.1
  • Package Namespace: Legacy javax.xml.bind.*
  • Compile-Time JDK Baseline: Java 8 / 11 (Major Version 52.0).
  • Java 21 Compatibility Status: Incompatible / Migration Required.
    • JDK Module Removal: JAXB was deprecated in Java 9 and fully removed from the standard JDK in Java 11 (JEP 320). While jaxb-api:2.3.1 provides standalone API classes, its use of the legacy javax.xml.bind.* namespace creates a hard blocker for modern frameworks.
    • Namespace Blocker (javax vs jakarta): Version 2.3.1 provides classes under javax.xml.bind.. Modern application stacks—including Spring Boot 3.x+, Spring Framework 6.0+/7.0+, and Jakarta EE 9+—require the jakarta.xml.bind. namespace and fail with ClassNotFoundException or NoClassDefFoundError when attempting to resolve JAXB contexts.
    • JPMS SPI Discovery: ServiceLoader discovery mechanism for JAXBContextFactory triggers JPMS reflection warnings or access failures under Java 21 unless explicitly opened or replaced with a modular runtime.

Key Architectural & Technical Characteristics on Java 21

  • Jakarta XML Binding Namespace Shift (javax.xml.bind $\rightarrow$ jakarta.xml.bind):
    • Legacy annotations like @XmlElement, @XmlRootElement, and @XmlType inside jaxb-api:2.3.1 belong to javax.xml.bind.
    • Frameworks on Spring 6+/7+ ignore javax.xml.bind annotations entirely during XML marshalling/unmarshalling, resulting in unparsed payloads or runtime context creation failures.
  • Runtime Implementation Requirement (API vs. Implementation):
    • jaxb-api contains only interfaces and factory loaders. It requires a runtime implementation provider (such as GlassFish JAXB or Eclipse Metro) to perform XML binding.
    • Under Java 21, JAXBContext.newInstance(...) searches for implementation factories via ServiceLoader or META-INF/services. Legacy implementations fail under strong JPMS module encapsulation unless updated alongside the API.
  • Virtual Threads Compatibility (Project Loom):
    • Unmarshalling XML payloads using JAXBContext is computationally intensive but thread-safe if context instances are reused. It does not block or pin carrier threads during execution.

JVM Command-Line Flag Options (Temporary Workaround Only)

If forced to execute javax.xml.bind:jaxb-api:2.3.1 on Java 21 during transition phases:

# Allow reflective factory lookup for JAXB SPI providers
--add-opens java.xml.bind/javax.xml.bind=ALL-UNNAMED
--add-opens java.base/java.la
Enter fullscreen mode Exit fullscreen mode

Artifact: org.glassfish.jaxb:jaxb-runtime:2.3.9

Original JDK Target & Baseline Requirements

  • Group ID: org.glassfish.jaxb
  • Artifact ID: jaxb-runtime
  • Version: 2.3.9
  • Package Namespace: Legacy javax.xml.bind.* (supported via jakarta.xml.bind-api 2.3.x)
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0).
  • Java 21 Compatibility Status: Conditional / Upgrade Required.
    • Java 21 Runtime Operations: Runs on Java 21 without fatal bytecode crashes because 2.3.9 includes maintenance fixes for JDK 17+ reflective access.
    • Namespace Blocker (javax vs jakarta): JAXB Runtime 2.3.9 generates context factory bindings for javax.xml.bind.. It is incompatible with Spring Boot 3.x+, Spring Framework 6.0+/7.0+, and Jakarta EE 9+ frameworks, which require jakarta.xml.bind..
    • JPMS Strong Encapsulation: Code generators (com.sun.xml.bind.v2.runtime.reflect.opt.Injector) inside 2.3.x use dynamic bytecode generation and internal JDK reflection (sun.misc.Unsafe), which triggers JPMS warnings or requires --add-opens flags on strict Java 21 runtimes.

Key Architectural & Technical Characteristics on Java 21

  • Jakarta EE Namespace Mismatch (javax.xml.bind $\rightarrow$ jakarta.xml.bind):
    • Version 2.3.9 implements the Java EE 8 / Jakarta EE 8 specification (javax.xml.bind).
    • Upgrading applications to Spring Boot 3+ / Spring 6+ causes JAXBContext initialization to fail because the runtime expects javax.xml.bind.JAXBContextFactory instead of jakarta.xml.bind.JAXBContextFactory.
  • Bytecode Generation & Class Injection (Unsafe / Reflection):
    • The 2.3.x runtime uses dynamic fast-accessor generation (FastAccessorAllocator) that accesses sun.misc.Unsafe to bypass constructor invocation.
    • Under Java 21's strong encapsulation, unless JVM flags are passed, fallback reflection mode is engaged, causing performance degradation during heavy marshalling/unmarshalling.
  • Virtual Threads Performance (Project Loom):
    • Concurrent XML parsing/marshalling via Marshaller and Unmarshaller is cpu-bound and safe for Virtual Threads. However, shared JAXBContext instances must be reused safely to prevent contention.

JVM Command-Line Flag Options (Temporary Workaround Only)

If running org.glassfish.jaxb:jaxb-runtime:2.3.9 on Java 21 under legacy javax.xml.bind frameworks:

# Permit dynamic class injection and reflection on internal JDK structures
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Artifact: com.solacesystems:sol-jms:10.6.0

Original JDK Target & Baseline Requirements

Group

ID: com.solacesystems

Artifact

ID: sol-jms

Version: 10.6.0

Package

Namespace: Legacy javax.jms.*

Compile-Time

JDK Baseline: Java 8 (Major Version 52.0).

Java 21 Compatibility

Status: Incompatible / Upgrade Required.

21 Runtime Operations: Version 10.6.0 was released prior to JDK 17/21 modularity updates and carries outdated bytecode/reflection dependencies that can lead to runtime warnings or failures under Java 21.

Namespace

Blocker (javax vs jakarta): sol-jms 10.6.0 utilizes the legacy javax.jms.* namespace. It is incompatible with Spring Boot 3.x+, Spring Framework 6.0+/7.0+, and Jakarta EE 9+ frameworks, which strictly require jakarta.jms.*.

JPMS

Strong Encapsulation: Legacy Solace JMS transport and reflection handlers trigger strong encapsulation warnings or reflection access errors under modern JDK runtimes unless explicitly permitted.

Key Architectural & Technical Characteristics on Java 21

Jakarta

EE Namespace Mismatch (javax.jms -> jakarta.jms):

Version 10.6.0 implements the legacy JMS 1.1 specification using the javax.jms package namespace.

Upgrading applications to Spring Boot 3+ or Jakarta EE 10+ causes ConnectionFactory and JMS template initializations to fail due to class definition mismatches between javax.jms.* and jakarta.jms.*.

Bytecode

Generation & Reflection:

The 10.6.0 client uses legacy internal serialization and socket-binding mechanisms that rely on unrestricted deep reflection.

Under Java 21's strict Module System boundaries, unconfigured access to internal JDK structures can cause security and reflective access exceptions.

Virtual

Threads Performance (Project Loom):

Legacy Solace JMS socket connection management and synchronous consumer polling rely heavily on thread blocking primitives. Running these on Virtual Threads under Java 21 can lead to thread pinning if synchronized blocks inside old transport layers are encountered.

Remediation & Migration Options

Modern

Jakarta EE 10+ / Spring Boot 3.x Path (Recommended):

 <dependency>
     <groupId>com.solacesystems</groupId>
     <artifactId>sol-jms-jakarta</artifactId>
     <version>10.30.1</version>
 </dependency>
Enter fullscreen mode Exit fullscreen mode

Legacy

javax.jms Maintenance Path (In-place Upgrade):

 <dependency>
     <groupId>com.solacesystems</groupId>
     <artifactId>sol-jms</artifactId>
     <version>10.30.2</version>
 </dependency>
Enter fullscreen mode Exit fullscreen mode

Artifact: com.ibm:com.ibm.mq.jmqi:7.5.0.6

Original JDK Target & Baseline Requirements

  • Group ID: com.ibm
  • Artifact ID: com.ibm.mq.jmqi
  • Version: 7.5.0.6 (Analyzed baseline: IBM MQ 7.5.x Java client libraries)
  • Compile-Time JDK Baseline: Java 6 / Java 7 (Major Version 50.0 / 51.0).
  • Java 21 Compatibility Status: Incompatible / Critical Upgrade Required.
    • Legacy IBM MQ 7.5.x Releases (< 9.3.x / 9.4.x): Fail on Java 21 runtimes due to deprecated native JNI bindings, strict JPMS module access restrictions, removed SecurityManager calls, and missing TLS 1.3 / modern cryptographic cipher support.
    • Modern IBM MQ All-Client Releases (>= 9.3.0 / 9.4.x - e.g., com.ibm.mq.jakarta.client): Fully compatible with Java 21, supporting Jakarta EE 10+ (jakarta.jms.* namespace), Spring Boot 3.x, and JPMS modular encapsulation.

Key Architectural & Technical Characteristics on Java 21

1. SecurityManager Removal & Reflection Restrictions (JPMS)

  • Older versions of IBM MQ (7.5.0.6) heavily rely on internal system property checks, custom security providers, and unexported JDK APIs (sun.misc.Unsafe / sun.security.*).
  • Under Java 21's strong encapsulation, legacy com.ibm.mq.jmqi binaries fail during channel initialization with InaccessibleObjectException or IllegalAccessError when attempting deep reflection.

2. javax.jms vs jakarta.jms Namespace Mismatch

  • Version 7.5.0.6 targets the legacy JMS 1.1 specification under the javax.jms.* package namespace.
  • Modern Java 21 application runtimes leveraging Spring Boot 3.x, Spring Framework 6.0+/7.0+, or Jakarta EE 10+ strictly require the jakarta.jms.* package space provided by modern IBM MQ client artifacts (such as com.ibm.mq.jakarta.client).

3. Virtual Threads Performance Optimization (Project Loom)

  • The 7.5.0.6 MQ client uses legacy synchronous socket polling and deep synchronized methods within socket/channel transport layers, causing carrier thread pinning under Virtual Threads on Java 21.
  • Modern IBM MQ 9.3+ / 9.4+ clients update socket transport locks to ReentrantLock primitives to prevent thread pinning.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older IBM MQ 7.5.0.6 binaries on Java 21 during transition phases under legacy javax.jms frameworks:

# Bypass JPMS encapsulation checks for legacy IBM MQ transport structures
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.net
Enter fullscreen mode Exit fullscreen mode

Artifact: com.gemstone.gemfire:gemfire:8.2.7

Original JDK Target & Baseline Requirements

  • Group ID: com.gemstone.gemfire
  • Artifact ID: gemfire
  • Version: 8.2.7 (Analyzed baseline: Legacy Pivotal/GemStone GemFire 8.x releases)
  • Compile-Time JDK Baseline: Java 7 / Java 8 (Major Version 51.0 / 52.0).
  • Java 21 Compatibility Status: Incompatible / Critical Migration Required.
    • Legacy GemFire 8.2.7 Releases: Fail completely on Java 21 due to deep dependencies on deprecated/removed JDK internals (sun.misc.Unsafe, sun.nio.ch.DirectBuffer), obsolete SecurityManager invocations, illegal reflective access across module boundaries, and legacy Spring/Java EE bindings.
    • Modern Tanzu GemFire / Apache Geode Releases (>= 10.x / Geode 1.15+): Required for Java 17/21 execution, JPMS strong encapsulation compatibility, and modern Jakarta EE / Spring Boot 3.x integration.

Key Architectural & Technical Characteristics on Java 21

1. JPMS Strong Encapsulation & Internal Bytecode Access

  • GemFire 8.2.7 relies heavily on sun.misc.Unsafe and internal NIO channel buffers for direct off-heap memory management and fast custom serialization (DataSerializer/PdxSerializer).
  • Under Java 21's strict Module System boundaries, invocation of unexported JDK internal classes triggers InaccessibleObjectException or severe runtime crashes unless extensive JVM --add-opens flags are supplied.

2. SecurityManager Removal & System Property Inspection

  • Older GemFire 8.x architectures utilize java.lang.SecurityManager for cluster member authentication, peer-to-peer security contexts, and internal property evaluations.
  • Because Java 21 severely restricts SecurityManager capabilities (throwing errors when configured strictly), legacy GemFire node initialization and cluster handshake mechanisms fail.

3. Virtual Threads & Thread Pinning (Project Loom)

  • GemFire 8.2.7 off-heap messaging and peer-to-peer distribution layers (JGroups/tcp transport) use extensive synchronized blocks for socket synchronization and message sequencing.
  • Executing GemFire operations on Virtual Threads under Java 21 will cause heavy carrier thread pinning, negating Virtual Thread concurrency gains.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run GemFire 8.2.7 on Java 21 during transition phases (Not recommended for production environments):

# Permit internal reflection and off-heap memory access required by GemFire 8.x
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-exports java.base/sun.misc=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Artifact: org.slf4j:slf4j-api:2.0.13

Original JDK Target & Baseline Requirements

  • Group ID: org.slf4j
  • Artifact ID: slf4j-api
  • Version: 2.0.13
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0).
  • Java 21 Compatibility Status: Compatible / Ready for Production.
    • Legacy SLF4J 1.7.x Releases: Utilize the static org.slf4j.impl.StaticLoggerBinder mechanism, which relies on old reflection lookups and is incompatible with JPMS modularization and Spring Boot 3.x / Jakarta EE 10+ environments.
    • Modern SLF4J 2.0.x Releases (>= 2.0.0 / 2.0.13+): Use the Java ServiceLoader architecture (java.util.ServiceLoader), fully supporting Java 21, JPMS module encapsulation, Virtual Threads, and modern logging backends (Logback 1.3+/1.5+, Log4j 2.20+).

Key Architectural & Technical Characteristics on Java 21

1. ServiceLoader Architecture & JPMS Modularization

  • SLF4J 2.0.13 uses java.util.ServiceLoader to locate SLF4J providers (SLF4JServiceProvider) dynamically at runtime rather than static class binding.
  • This approach complies natively with the Java Platform Module System (JPMS), avoiding illegal reflective access warnings and eliminating the need for custom --add-opens flags on Java 21.

2. Virtual Threads & Fluent Logging API (Project Loom)

  • Version 2.0.13 introduces the Fluent Logging API (logger.atDebug().log(...)), which optimizes object allocations and string formatting.
  • The logging API is completely thread-safe and stateless, ensuring zero carrier thread pinning when executed inside Project Loom Virtual Threads on Java 21.

3. Spring Framework 6.0/7.0 & Jakarta EE 10+ Compatibility

  • SLF4J 2.0.13 is the native default baseline for Spring Boot 3.x and Spring Framework 6+/7+.
  • It pairs directly with Logback 1.4.x / 1.5.x or Log4j 2.23.1+ (log4j-slf4j2-impl) to provide full Jakarta EE compatibility without namespace conflicts.

JVM Command-Line Flag Options (Legacy Workaround)

No JVM command-line flags are required for SLF4J 2.0.13 under Java 21.

If forced to run legacy SLF4J 1.7.x binaries on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for legacy static binder lookup
--add-opens java.base/java.lang=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Artifact: org.apache.tomcat.embed:tomcat-embed-core:10.1.28

Original JDK Target & Baseline Requirements

  • Group ID: org.apache.tomcat.embed
  • Artifact ID: tomcat-embed-core
  • Version: 10.1.28
  • Compile-Time JDK Baseline: Java 11 (Major Version 55.0) for Tomcat 10.1.x line.
  • Java 21 Compatibility Status: Fully Compatible / Production Ready.
    • Legacy Tomcat 9.x Releases (< 10.0.0): Rely on javax.servlet.* package namespace, incompatible with Spring Boot 3.x / Jakarta EE 10+ runtimes on Java 21.
    • Modern Tomcat 10.1.x Releases (>= 10.1.0 / 10.1.28+): Native support for Java 21, jakarta.servlet.* namespace (Servlet 6.0 spec), JPMS module boundaries, and Virtual Thread execution via Loom.

Key Architectural & Technical Characteristics on Java 21

1. Virtual Threads Support & Project Loom Integration

  • Tomcat 10.1.28 natively supports executing HTTP request-handling threads on Java 21 Virtual Threads (java.lang.Thread.ofVirtual()) without blocking underlying carrier threads.
  • Synchronization locks within socket processors and container endpoints in 10.1.x have been refactored to eliminate Virtual Thread pinning during I/O operations.

2. Jakarta EE 10 Namespace Alignment (jakarta.servlet)

  • Version 10.1.28 implements Jakarta Servlet 6.0, Jakarta Server Pages 3.1, and Jakarta WebSocket 2.1 specs.
  • Fully aligned with Spring Boot 3.x and Spring Framework 6.x/7.x baseline requirements, eliminating legacy javax.servlet namespace collision errors.

3. JPMS Compatibility & SecurityManager Lifecycle

  • Tomcat 10.1.28 removes dependency on legacy java.lang.SecurityManager calls that cause warning noise under Java 21 runtime environments.
  • Complies with JDK strong encapsulation rules without requiring --add-opens or --add-exports flags for web application context loading.

JVM Command-Line Flag Options (Legacy Workaround)

No command-line flags are required for Tomcat 10.1.28 on Java 21.

If forced to run legacy Tomcat 9.x binaries on Java 21 during transition phases:

# Permit internal reflection on reflection-based context loaders
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAM
Enter fullscreen mode Exit fullscreen mode

Artifact: org.apache.tomcat.embed:tomcat-embed-jasper:10.1.28

Original JDK Target & Baseline Requirements

  • Group ID: org.apache.tomcat.embed
  • Artifact ID: tomcat-embed-jasper
  • Version analyzed: 10.1.28 (Tomcat 10.1.x line)
  • Compile-Time JDK Baseline: Java 11 (Major Version 55.0) for all 10.1.x releases.
  • Specification Baseline: Jakarta EE 10 (Servlet 6.0, JSP 3.1, EL 5.0), jakarta.* namespace only.
  • Java 21 Compatibility Status: Compatible, with patch-level caveats.
    • Runtime on Java 21: Tomcat 10.1.x officially supports running on Java 21, and no --add-opens flags are required for embedded Jasper.
    • JSP compilation: Jasper compiles JSPs with the Eclipse JDT compiler (ECJ). ECJ must be recent enough to parse Java 21 source level, otherwise JSPs using modern syntax fail to compile at runtime.
  • Security: 10.1.28 predates several JSP and servlet-container CVE fixes (for example CVE-2024-50379 and CVE-2024-56337 on the JSP compilation path). Upgrade the patch version as part of the migration.

Key Architectural & Technical Characteristics on Java 21

1. Jakarta Namespace (Not a Java 21 Issue, but a Migration Blocker):

  • Tomcat 10.1.x uses jakarta.servlet., jakarta.servlet.jsp. and jakarta.el.*.
  • Any JSP, tag library, or custom tag handler still importing javax.servlet.* will fail to compile or load.
  • Check your TLDs and web.xml descriptors for old javax namespaces and old schema versions.

2. JSP Compilation with ECJ:

  • Jasper translates JSPs to Java source and compiles them at runtime with ECJ.
  • Make sure the ECJ version on the classpath supports Java 21 (ECJ 3.35 or newer). An outdated ECJ can reject Java 21 source or target levels.
  • If you pin compilerSourceVM / compilerTargetVM in the Jasper servlet init params, set them to 21 or remove them and let Jasper detect the running JVM.

3. Virtual Threads (Project Loom):

  • Tomcat 10.1.x can run request processing on virtual threads through its virtual-thread executor.
  • Runtime JSP compilation is heavy, synchronized work that can pin carrier threads.
  • Recommendation: precompile JSPs at build time (JspC) so no compilation happens on request threads under Java 21.

4. Spring Framework 7.0 / Jakarta EE 11 Alignment:

  • Tomcat 10.1.x implements Servlet 6.0 (Jakarta EE 10).
  • Spring Framework 7.0 and Jakarta EE 11 target Servlet 6.1, which means Tomcat 11.x.
  • Staying on Tomcat 10.1.x is fine for Java 21 with Spring Boot 3.x / Spring Framework 6.x.
  • Plan a move to tomcat-embed-jasper 11.x if you adopt Spring 7.0.

JVM Command-Line Flag Options

No legacy --add-opens workaround is needed for tomcat-embed-jasper 10.1.x on Java 21.

Only if your own code or third-party JSP tag libraries use reflection into JDK internals:

# Last resort only: open specific JDK packages for unnamed-module code
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Prefer fixing or upgrading the offending library over keeping these flags.

Recommended Build Configuration & Migration Strategy

Action Required: Move off 10.1.28 to the latest 10.1.x patch release (at minimum 10.1.34 for the JSP compilation security fixes).

Keep tomcat-embed-core, tomcat-embed-el, and tomcat-embed-jasper on the same version.

Maven Configuration

<properties>
    <java.version>21</java.version>
    <!-- Use latest 10.1.x patch; 10.1.34+ at minimum -->
    <tomcat.version>10.1.34</tomcat.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    tomcatVersion = '10.1.34' // latest 10.1.x patch recommended
}
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
dependencies {
    implementation "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}"
    implementation "org.apache.tomcat.embed:tomcat-embed-el:${tomcatVersion}"
    implementation "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Spring Boot Users

<!-- Override the Boot-managed version only if Boot lags behind the patch you need -->
<properties>
    <tomcat.version>10.1.34</tomcat.version>
</properties>
Enter fullscreen mode Exit fullscreen mode

Pre-Production Migration Checklist

  • Replace all javax.servlet. / javax.el. imports and TLD references with jakarta.*.
  • Align all tomcat-embed-* artifacts on one version.
  • Confirm the ECJ version on the classpath supports Java 21.
  • Precompile JSPs (JspC) in the build to avoid runtime compilation and virtual-thread pinning.
  • Run the full JSP regression suite on a Java 21 JVM before cutover.
  • Scan third-party tag libraries for javax usage and deep reflection.

Artifact: com.fasterxml.jackson.core:jackson-databind

Original JDK Target & Baseline Requirements

  • Group ID: com.fasterxml.jackson.core
  • Artifact ID: jackson-databind
  • Version: ${jackson.version} (Analyzed baseline: Jackson 2.x releases < 2.15.0; image specified version 2.17.2)
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0) for Jackson 2.x releases.
  • Java 21 Compatibility Status: Conditional / Version-Dependent.
    • Legacy Jackson 2.x Releases (< 2.15.0): Fail or issue runtime warnings on Java 21 due to illegal reflective access to private record components, JDK internal classes, and lack of native support for Java 21 features like Record patterns or Virtual Threads.
    • Modern Jackson 2.x Releases (>= 2.15.0 / 2.17.2+): Fully compatible with Java 21, providing full support for Java Records, modern JPMS module encapsulation, and Spring Framework 6.x / 7.0 & Jakarta EE 10 / 11 runtime environments.

Key Architectural & Technical Characteristics on Java 21

1. JPMS Encapsulation & Reflection on Private Members

  • Older Jackson versions rely heavily on deep reflection (setAccessible(true)) to access private fields and constructors.
  • Under Java 21, strong encapsulation enforced by JPMS prohibits illegal reflective access to JDK internal types, triggering InaccessibleObjectException unless explicit reflective access is granted or modern Jackson constructors are used.

2. Record Classes & Virtual Threads Optimization

  • Java 21 formalizes Record classes and Virtual Threads (Project Loom). Older Jackson releases (< 2.15.0) struggle to introspect Record constructors without explicit annotations or require additional parameters.
  • Jackson 2.15.0+ natively handles Record deserialization without byte-buddy or runtime proxy issues and avoids synchronized block contention on carrier threads during serialization.

3. Spring Framework 6.x/7.0 & Jakarta EE Compatibility

  • Modern Jackson 2.17.2 integrates seamlessly with Spring Boot 3.x / Spring 6.x+ and Jakarta RESTful Web Services.
  • Ensure corresponding module versions (jackson-datatype-jsr310, jackson-module-parameter-names) match the target 2.17.2 baseline to prevent binary mismatch during JSON parsing.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Jackson binaries (< 2.15.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal property lookup and reflection
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Jackson Version 2.17.2+ Ensure ${jackson.version} is defined as 2.17.2 (or latest 2.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Jackson version to a Java 21 / Virtual Thread safe baseline -->
    <jackson.version>2.17.2</jackson.version>
</properties>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jacksonVersion = '2.17.2'
}

dependencies {
    implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.fasterxml.jackson.core:jackson-databind

Original JDK Target & Baseline Requirements

  • Group ID: com.fasterxml.jackson.core
  • Artifact ID: jackson-databind
  • Version: ${jackson.version} (Analyzed baseline: Jackson 2.x releases < 2.15.0; image specified version 2.17.2)
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0) for Jackson 2.x releases.
  • Java 21 Compatibility Status: Conditional / Version-Dependent.
    • Legacy Jackson 2.x Releases (< 2.15.0): Fail or issue runtime warnings on Java 21 due to illegal reflective access to private record components, JDK internal classes, and lack of native support for Java 21 features like Record patterns or Virtual Threads.
    • Modern Jackson 2.x Releases (>= 2.15.0 / 2.17.2+): Fully compatible with Java 21, providing full support for Java Records, modern JPMS module encapsulation, and Spring Framework 6.x / 7.0 & Jakarta EE 10 / 11 runtime environments.

Key Architectural & Technical Characteristics on Java 21

1. JPMS Encapsulation & Reflection on Private Members

  • Older Jackson versions rely heavily on deep reflection (setAccessible(true)) to access private fields and constructors.
  • Under Java 21, strong encapsulation enforced by JPMS prohibits illegal reflective access to JDK internal types, triggering InaccessibleObjectException unless explicit reflective access is granted or modern Jackson constructors are used.

2. Record Classes & Virtual Threads Optimization

  • Java 21 formalizes Record classes and Virtual Threads (Project Loom). Older Jackson releases (< 2.15.0) struggle to introspect Record constructors without explicit annotations or require additional parameters.
  • Jackson 2.15.0+ natively handles Record deserialization without byte-buddy or runtime proxy issues and avoids synchronized block contention on carrier threads during serialization.

3. Spring Framework 6.x/7.0 & Jakarta EE Compatibility

  • Modern Jackson 2.17.2 integrates seamlessly with Spring Boot 3.x / Spring 6.x+ and Jakarta RESTful Web Services.
  • Ensure corresponding module versions (jackson-datatype-jsr310, jackson-module-parameter-names) match the target 2.17.2 baseline to prevent binary mismatch during JSON parsing.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Jackson binaries (< 2.15.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal property lookup and reflection
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Jackson Version 2.17.2+ Ensure ${jackson.version} is defined as 2.17.2 (or latest 2.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Jackson version to a Java 21 / Virtual Thread safe baseline -->
    <jackson.version>2.17.2</jackson.version>
</properties>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jacksonVersion = '2.17.2'
}

dependencies {
    implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: junit:junit

Original JDK Target & Baseline Requirements

  • Group ID: junit
  • Artifact ID: junit
  • Version: ${junit.version} (Analyzed baseline: JUnit 4.x releases < 4.13.2; image specified version 4.12)
  • Compile-Time JDK Baseline: Java 5 / 6 (Major Version 49.0 / 50.0) for standard JUnit 4.x releases.
  • Java 21 Compatibility Status: Conditional / Migration Recommended.
    • Legacy JUnit 4 Releases (< 4.13.2): Fail or issue severe warnings on Java 21 due to restricted access to internal reflective fields, @Rule / @test(expected) reflection failures under JPMS encapsulation, and security vulnerabilities (e.g., insecure temporary directory creation in TemporaryFolder).
    • Modern JUnit 4 Releases (>= 4.13.2): Minimal compatibility fixes applied to run under Java 21, but lacks native support for modern Java 21 features (such as Virtual Threads testing, dynamic test generation, and modern execution listeners).
    • Target Baseline (JUnit Jupiter / JUnit 5): Upgrade to org.junit.jupiter:junit-jupiter (5.10.x+) or use org.junit.vintage:junit-vintage-engine (5.10.x+) for full Java 21 and build tool integration.

Key Architectural & Technical Characteristics on Java 21

1. SecurityManager Deprecation & Internal Reflection Restrictions (JPMS)

  • Legacy JUnit 4 rules rely on internal reflective access to private test fields, exception handlers, and security checks.
  • Under Java 21, strong encapsulation enforced by JPMS prohibits illegal reflective access, resulting in InaccessibleObjectException when running legacy custom rules or runners without modern JUnit Jupiter lifecycle extensions.

2. TemporaryFolder Vulnerabilities & JDK File Handling

  • JUnit 4's TemporaryFolder rule prior to 4.13.2 contains known security vulnerabilities and utilizes legacy file system operations that trigger warning flags under modern Java security profiles.
  • JUnit 5 @TempDir replaces this with non-blocking, POSIX-compliant, and Virtual Thread safe temporary file handling.

3. Virtual Threads & Concurrency Execution (Project Loom)

  • Running multi-threaded JUnit 4 suites with custom runners on Java 21 carrier threads can lead to thread pinning if standard synchronization blocks are used inside test execution mechanisms.
  • JUnit Jupiter (JUnit 5) provides native parallel test execution options optimized for concurrent, Virtual Thread driven execution pipelines.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older JUnit 4.x binaries (< 4.13.2) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal reflective access during test execution
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Upgrade to JUnit 4.13.2 baseline or migrate to JUnit Jupiter 5.10.x+ Ensure ${junit.version} is updated to at least 4.13.2 for legacy bridges, or transition directly to JUnit 5 (junit-jupiter).

Maven Configuration

<properties>
    <!-- Set JUnit version to a Java 21 compatible baseline -->
    <junit.version>4.13.2</junit.version>
</properties>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    junitVersion = '4.13.2'
}

dependencies {
    testImplementation "junit:junit:${junitVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: jfree:jfreechart

Original JDK Target & Baseline Requirements

  • Group ID: jfree
  • Artifact ID: jfreechart
  • Version: ${jfreechart.version} (Analyzed baseline: legacy 1.0.x releases < 1.5.0; image specified version 1.0.13)
  • Compile-Time JDK Baseline: Java 1.4 / 5 (Major Version 48.0 / 49.0) for legacy jfree:jfreechart 1.0.x releases.
  • Java 21 Compatibility Status: Legacy / High Risk (Action Required: Maven Group ID & Major Version Migration).
    • Legacy Releases (jfree:jfreechart 1.0.x): Highly problematic under Java 21 due to obsolete AWT/Swing internal dependencies, hardcoded reflection calls, lack of JPMS module descriptors, and deprecated graphics/font rendering calls.
    • Modern Releases (org.jfree:jfreechart 1.5.x+): Relocated under the org.jfree Group ID. Modern releases (1.5.0+) remove legacy dependencies (like jcommon), fully support modern JDKs (Java 11/17/21), support modular runtime execution (JPMS), and fix high-DPI scaling and graphics pipeline compatibility.

Key Architectural & Technical Characteristics on Java 21

1. Artifact Relocation & Dependency Cleanup

  • Legacy jfree:jfreechart versions depend on jfree:jcommon, an obsolete utility library that relies on reflection and old Java desktop internals.
  • Modern JFreeChart (1.5.x+) consolidated code, eliminated the jcommon dependency entirely, and relocated to org.jfree:jfreechart.

2. Desktop AWT/Graphics Environment & Headless Execution

  • Under Java 21, running AWT/Swing rendering in headless environments (e.g., microservices, CI/CD pipelines, containerized environments) requires proper configuration to prevent HeadlessException.
  • Modern releases properly handle headless graphics context initialization for server-side chart/image rendering (PNG, JPEG, SVG).

3. JPMS Encapsulation & Virtual Threads Integration

  • Legacy 1.0.x versions perform direct internal access to JDK system properties and graphics internals, throwing InaccessibleObjectException under Java 21.
  • Version 1.5.x+ adds module-info.java support (org.jfree.jfreechart), making it fully compatible with Java 21 module path boundaries without requiring forced --add-opens flags.

JVM Command-Line Flag Options (Legacy Workaround)

If temporarily forced to run legacy jfree:jfreechart binaries (< 1.5.0) on Java 21:

# Force headless rendering mode for server environments
-Djava.awt.headless=true
# Bypass JPMS encapsulation checks for legacy desktop components
--add-opens java.desktop/sun.awt=ALL-UNNAMED
--add-opens java.desktop/java.awt=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Migrate Group ID from jfree to org.jfree and enforce Version 1.5.4+ Update build configurations to use org.jfree:jfreechart (version 1.5.4 or higher) and remove obsolete jfree:jcommon dependencies from management blocks.

Maven Configuration

<properties>
    <!-- Set JFreeChart version to a Java 21 / JPMS modular baseline -->
    <jfreechart.version>1.5.4</jfreechart.version>
</properties>

<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jfreechart</artifactId>
    <version>${jfreechart.version}</version>
    <scope>test</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jfreechartVersion = '1.5.4'
}

dependencies {
    testImplementation "org.jfree:jfreechart:${jfreechartVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.google.caliper:caliper

Original JDK Target & Baseline Requirements

  • Group ID: com.google.caliper
  • Artifact ID: caliper
  • Version: ${caliper.version} (Analyzed baseline: legacy microbenchmarking releases < 1.0-beta-3; image specified version 0.5-rc1)
  • Compile-Time JDK Baseline: Java 6 / 7 (Major Version 50.0 / 51.0) for legacy com.google.caliper:caliper releases.
  • Java 21 Compatibility Status: Incompatible / Deprecated (Migration to JMH strongly recommended).
    • Legacy Caliper Releases (0.5-rc1 / early 1.0 snapshots): Severely broken under Java 21 due to removed internal APIs (sun.misc.Unsafe), legacy JVM execution flags, strict JPMS module encapsulation, and reliance on obsolete Guava and ByteCode manipulation libraries.
    • Modern Alternatives (OpenJDK JMH): Because Google Caliper development is largely dormant and unmaintained for modern JDKs, standard Java microbenchmarking on Java 21 requires migrating to OpenJDK JMH (org.openjdk.jmh:jmh-core).

Key Architectural & Technical Characteristics on Java 21

1. JPMS Encapsulation & Internal Bytecode Instrumentation

  • Caliper dynamically allocates memory and inspects JVM bytecode using deep reflection and low-level internal flags.
  • Under Java 21, JPMS strong encapsulation causes InaccessibleObjectException when Caliper attempts to reflect into system classes or access internal JVM runner mechanisms.

2. Removed SecurityManager & Legacy JVM Process Forking

  • Caliper spawns worker sub-JVMs using command-line arguments that rely on deprecated or removed JVM options (such as -XX:+UseCompressedOops flags or obsolete GC tuning).
  • Reliance on java.lang.SecurityManager for worker process execution causes runtime failures under Java 21's strict SecurityManager deprecation profile.

3. Virtual Threads (Project Loom) & Modern Microbenchmarking

  • Modern performance profiling on Java 21 requires measuring thread pinning and thread-per-task behaviors under Project Loom.
  • Caliper's legacy runner architecture cannot accurately measure Virtual Thread context switches or modern GC behavior (e.g., ZGC or Generational Shenandoah), whereas OpenJDK JMH natively supports Java 21 features.

JVM Command-Line Flag Options (Legacy Workaround)

If temporarily forced to run legacy Caliper benchmarks (< 1.0-beta-3) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for bytecode reflection and Unsafe lookup
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-exports java.base/sun.misc=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Upgrade to Caliper 1.0-beta-3 or migrate to OpenJDK JMH (Recommended for Java 21) To safely execute benchmarks under Java 21, replace Caliper with OpenJDK JMH or enforce the latest available snapshot in test dependencies.

Maven Configuration (JMH Recommended Replacement)

<properties>
    <!-- Set JMH version to a fully supported Java 21 microbenchmarking baseline -->
    <jmh.version>1.37</jmh.version>
</properties>

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>${jmh.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>${jmh.version}</version>
    <scope>test</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jmhVersion = '1.37'
}

dependencies {
    testImplementation "org.openjdk.jmh:jmh-core:${jmhVersion}"
Enter fullscreen mode Exit fullscreen mode

testAnnotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:${jmhVersion}"

}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.oracle.database.jdbc:ojdbc11

Original JDK Target & Baseline Requirements

  • Group ID: com.oracle.database.jdbc
  • Artifact ID: ojdbc11
  • Version: ${ojdbc.version} (Analyzed baseline: Oracle JDBC 21c / 23c releases; image specified version 23.5.0.24.07)
  • Compile-Time JDK Baseline: Java 11 (Major Version 55.0) minimum for ojdbc11.
  • Java 21 Compatibility Status: Fully Compatible / Recommended Baseline.
    • Legacy Oracle JDBC Drivers (ojdbc8 / ojdbc10 / ojdbc11 < 21.3): Fail or experience Virtual Thread carrier thread pinning under Java 21 due to legacy synchronized network/socket I/O blocks, security provider reflection, and lack of native support for modern JDBC 4.3 features under JPMS encapsulation.
    • Modern Oracle JDBC Drivers (ojdbc11 >= 21.9 / 23.x): Fully compatible with Java 21, JPMS modular runtime environments, Virtual Threads (Project Loom), and Spring Framework 6.x / 7.0 & Jakarta EE 10 / 11 persistence layers.

Key Architectural & Technical Characteristics on Java 21

1. Virtual Threads Performance Optimization (Project Loom)

  • Older Oracle JDBC drivers utilize synchronized blocks around TCP socket reads/writes and Oracle Net Protocol handlers, causing severe Virtual Thread Pinning on Java 21 carrier threads.
  • Oracle JDBC 23c (23.x.x.x) refactored internal locking and socket I/O to be Virtual Thread-friendly, allowing database connection calls to yield cleanly without blocking carrier threads.

2. SecurityManager Deprecation & JPMS Module Encapsulation

  • Oracle JDBC drivers historically accessed system properties and security providers using reflection or SecurityManager APIs.
  • Modern ojdbc11 binaries include explicit JPMS module descriptors (oracle.jdbc) and operate safely within strict Java 21 module boundaries without requiring illegal reflective access flags.

3. Spring Framework 6.x / 7.0 & HikariCP Integration

  • Integrates seamlessly with Spring Boot 3.x / Spring 6.x+ and HikariCP connection pooling under Jakarta EE runtime environments.
  • Supports high-performance features like Oracle Native Vector Search (23c AI vector data types) and modern TLS 1.3 database connections on Java 21.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Oracle JDBC binaries (ojdbc8 or early ojdbc11) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal property and security provider lookups
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Oracle JDBC Version 23.5.0.24.07+ Ensure ${ojdbc.version} is defined as 23.5.0.24.07 (or latest 23c release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Oracle JDBC version to a Java 21 / Virtual Thread safe baseline -->
    <ojdbc.version>23.5.0.24.07</ojdbc.version>
</properties>

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc11</artifactId>
    <version>${ojdbc.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    ojdbcVersion = '23.5.0.24.07'
}

dependencies {
    implementation "com.oracle.database.jdbc:ojdbc11:${ojdbcVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.fasterxml.jackson.core:jackson-annotations

Original JDK Target & Baseline Requirements

  • Group ID: com.fasterxml.jackson.core
  • Artifact ID: jackson-annotations
  • Version: ${jackson-annotations.version} (Analyzed baseline: Jackson 2.x releases < 2.15.0; image specified version 2.17.2)
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0) for Jackson 2.x releases.
  • Java 21 Compatibility Status: Fully Compatible / Recommended Baseline.
    • Legacy Jackson 2.x Releases (< 2.15.0): Lack native support for Java 21 Record class component metadata and pattern matching annotations, leading to version mismatch warnings when paired with modern core components.
    • Modern Jackson 2.x Releases (>= 2.15.0 / 2.17.2+): Fully compatible with Java 21, JPMS module encapsulation (com.fasterxml.jackson.annotation), Java Record constructors (@JsonCreator, @JsonProperty), and Spring Framework 6.x / 7.0 & Jakarta EE 10 / 11 runtime environments.

Key Architectural & Technical Characteristics on Java 21

1. JPMS Encapsulation & Automatic Module Naming

  • jackson-annotations defines a clean module boundary (com.fasterxml.jackson.annotation) compatible with Java 21 modular builds.
  • Aligning annotation versions with core modules (jackson-core, jackson-databind) prevents split-package errors and reflection access failures under JPMS.

2. Java Records & Modern Constructors

  • Jackson 2.17.2 annotations natively inspect record components, @JsonCreator modes (PROPERTIES, DELEGATE), and compact constructors introduced in modern Java versions without requiring byte-manipulation hacks or custom retention policies.

3. Transitive Dependency Alignment Across Microservices

  • Ensures consistent annotation processing across rest clients, DTOs, and serialization modules.
  • Upgrading jackson-annotations to 2.17.2 alongside jackson-databind prevents silent annotation ignore behavior caused by transitive version drift in multi-module builds.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Jackson annotation packages (< 2.15.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal property lookup and reflection
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Align Transitive jackson-annotations Version with 2.17.2+

Ensure ${jackson.version} is defined as 2.17.2 (or latest 2.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Jackson version to a Java 21 / Virtual Thread safe baseline -->
    <jackson.version>2.17.2</jackson.version>
</properties>

<!-- Align transitive jackson-annotations with jackson-core/databind baseline -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${jackson.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jacksonVersion = '2.17.2'
}

dependencies {
    implementation "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-core

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-core
  • Version: ${spring.version} (Analyzed baseline: Spring Framework 5.x releases < 6.0.0)
  • Compile-Time JDK Baseline: Java 8 (Major Version 52.0) for Spring Framework 5.x releases.
  • Java 21 Compatibility Status: Conditional / Major Version-Dependent.
    • Legacy Spring Framework Releases (5.x / Spring Boot 2.x): Fail or generate critical errors on Java 21 due to outdated ASM bytecode generation, incompatible CGLIB dynamic proxies, invalid class-file version parsing (bytecode version 65.0), and removed SecurityManager runtime calls.
    • Modern Spring Framework Releases (6.1.x+ / Spring Boot 3.2+): Fully compatible with Java 21, natively supporting Virtual Threads (Project Loom), Record patterns, JPMS module encapsulation, and Jakarta EE 10/11 specifications.

Key Architectural & Technical Characteristics on Java 21

1. Bytecode Generation & CGLIB Compatibility

  • Legacy spring-core 5.x packages embed older versions of ASM and CGLIB that cannot parse Java 21 class file formats (major version 65), causing IllegalArgumentException during bean introspection and AOP proxy generation.
  • Spring Core 6.1+ updates internal ASM to version 9.6+, allowing smooth reflection, record component inspection, and dynamic subclassing under Java 21.

2. Virtual Threads & TaskExecutor Integration (Project Loom)

  • Spring Core 6.1 introduced native support for Virtual Threads via SimpleAsyncTaskExecutor and VirtualThreadTaskExecutor.
  • Internal synchronization blocks across core resource loaders and bean definitions were refactored to prevent carrier thread pinning during concurrent bean initialization and event publishing.

3. SecurityManager Removal & JPMS Module Encapsulation

  • Spring 5.x relied on java.lang.SecurityManager checks in LocalVariableTableParameterNameDiscoverer and reflection accessors.
  • Java 21 deprecates SecurityManager for removal; Spring Core 6.x completely removed SecurityManager invocations and standardizes on JPMS modular access without requiring forced --add-opens flags.

JVM Command-Line Flag Options (Legacy Workaround)

If temporarily forced to run legacy Spring Framework 5.3.x binaries on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for internal property and reflection lookup
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Upgrade to Spring Framework 6.1.x+ (or Spring Boot 3.2+) Ensure ${spring.version} is updated to at least 6.1.0 (or 6.1.14+) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Spring Framework version to a Java 21 / Virtual Thread safe baseline -->
    <spring.version>6.1.14</spring.version>
</properties>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    springVersion = '6.1.14'
}

dependencies {
    implementation "org.springframework:spring-core:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-beans

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-beans
  • Version: ${spring.version} (Analyzed baseline: spring.version: 7.0.9)
  • Compile-Time JDK Baseline: Java 17+ (Major Version 61.0+) for Spring Framework 6.x/7.x releases.
  • Java 21 Compatibility Status: Fully Compatible / Production Baseline.
  • Legacy Spring Framework Releases (< 6.0.0): Fail or generate warnings under Java 21 due to outdated ASM bytecode generation, incompatible CGLIB bean proxy instantiation, invalid class-file version parsing (bytecode version 65.0), and removed SecurityManager runtime calls.
  • Modern Spring Framework Releases (>= 6.1.0 / 7.0.9): Natively designed for Java 21 LTS, fully supporting Virtual Threads (Project Loom), Record patterns, JPMS module encapsulation, and Jakarta EE 10/11 runtime environments.

Key Architectural & Technical Characteristics on Java 21

Bytecode Generation & Dynamic Proxy Refactoring:

  • Spring Beans relies heavily on ASM and dynamic proxies for bean instantiation, setter injection, and factory methods.
  • Versions 6.1+ and 7.0.x incorporate internal ASM upgrades capable of parsing Java 21 class file formats (major version 65.0) and inspecting constructor parameters without requiring deprecated bytecode manipulation hacks.

Virtual Threads & Concurrent Bean Factory Access (Project Loom):

  • Older bean factory resolution logic utilized coarse synchronization around singleton bean registries and factory bean creation methods, causing Virtual Thread Pinning on Java 21 carrier threads.
  • Modern Spring 6.1+/7.0.x refactored bean initialization and locks to utilize concurrent collections and modern locks, ensuring thread safety without blocking Virtual Thread carrier threads.

Java Records & Constructor Injection Encapsulation:

  • Modern spring-beans provides direct, native reflection and parameter-name retention handling for Java Records and compact record constructors.
  • Eliminates the need for explicit @ConstructorProperties annotations when auto-wiring record-based bean definitions on Java 21.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Spring Beans binaries (< 6.0.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for reflective property access and bean instantiation
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Spring Framework Baseline 7.0.9

Ensure ${spring.version} is defined as 7.0.9 (or latest Spring 6.1+/7.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Spring Framework version to a Java 21 safe baseline -->
    <spring.version>7.0.9</spring.version>
</properties>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    springVersion = '7.0.9'
}

dependencies {
    implementation "org.springframework:spring-beans:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-context

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-context
  • Version: ${spring.version} (Analyzed baseline: spring.version: 7.0.9)
  • Compile-Time JDK Baseline: Java 17+ (Major Version 61.0+) for Spring Framework 6.x/7.x releases.
  • Java 21 Compatibility Status: Fully Compatible / Production Baseline.
  • Legacy Spring Context Releases (< 6.0.0): Fail or generate startup errors under Java 21 due to outdated CGLIB proxy generation, incompatible reflection over internal JDK modules, and deprecated SecurityManager runtime calls.
  • Modern Spring Context Releases (>= 6.1.0 / 7.0.9): Natively designed for Java 21 LTS, fully supporting Virtual Threads (Project Loom) task executors, dynamic proxy refactoring, JPMS module encapsulation, and Jakarta EE 10/11 runtime environments.

Key Architectural & Technical Characteristics on Java 21

  • Virtual Thread Task Executor Integration (Project Loom):
  • Spring Context 6.1+ introduces native support for Virtual Threads in application listener dispatching, @async execution, and scheduled tasks via SimpleAsyncTaskExecutor and VirtualThreadTaskExecutor.
  • Refactored internal event publication locking mechanisms to use modern concurrency primitives, preventing carrier thread pinning during high-throughput event processing.

SecurityManager Removal & JPMS Module Access:

  • Spring Context previously relied on SecurityManager for privilege checks when configuring application contexts, class scanning, and environment property resolution.
  • Because Java 21 deprecates SecurityManager for removal, Spring Context 7.0.9 fully removes legacy security checks and adheres cleanly to JPMS module encapsulation without requiring custom --add-opens flags.

Advanced Reflection & Record Component Wiring:

  • Spring Context natively scans and registers Java Record components, record-based @ConfigurationProperties, and modern annotation stereotypes.
  • Upgraded internal ASM framework allows direct parsing of Java 21 bytecode (major version 65.0) during component scanning without requiring bytecode manipulation workarounds.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older Spring Context binaries (< 6.0.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for component scanning and dynamic proxy generation
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Spring Framework Baseline 7.0.9

Ensure ${spring.version} is defined as 7.0.9 (or latest Spring 6.1+/7.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Spring Framework version to a Java 21 safe baseline -->
    <spring.version>7.0.9</spring.version>
</properties>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    springVersion = '7.0.9'
}

dependencies {
    implementation "org.springframework:spring-context:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-context-support

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-context-support
  • Version: ${spring.version} (Analyzed baseline: spring.version: 7.0.9)
  • Compile-Time JDK Baseline: Java 17+ (Major Version 61.0+) for Spring Framework 6.x / 7.x releases.
  • Java 21 Compatibility Status: Fully Compatible / Production Baseline.
    • Legacy Spring Context Support Releases (< 6.0.0): Fail or exhibit unexpected behavior on Java 21 due to outdated caching integration APIs, legacy Quartz/Caffeine bindings expecting pre-Jakarta namespaces, and removed SecurityManager calls.
    • Modern Spring Context Support Releases (>= 6.1.0 / 7.0.9): Natively targeted for Java 21 LTS, fully supporting Virtual Thread-aware task execution, modern Caffeine/Guava cache abstractions, JPMS encapsulation, and Jakarta EE 10/11 specifications.

Key Architectural & Technical Characteristics on Java 21

1. Virtual Thread Scheduling & Quartz Integration

  • spring-context-support provides integration classes for Quartz scheduling, Mail (JavaMailSender), and third-party caching providers.
  • In Spring Framework 6.1+ and 7.0.9, job scheduling and async mail submission abstractions natively support execution on Java 21 Virtual Threads (SimpleAsyncTaskExecutor configured with virtual threads), preventing thread pool exhaustion during I/O-heavy background tasks.

2. Cache Abstractions & Third-Party Compatibility

  • Integrations for modern caching libraries (Caffeine 3.x+, JCache/JSR-107) are fully upgraded to support Java 21 runtime semantics and Strong Encapsulation (JPMS).
  • Legacy integration classes bound to end-of-life frameworks (such as EhCache 2.x) have been purged in favor of Jakarta-compatible, JPMS-compliant abstractions.

3. SecurityManager Removal & JPMS Compliance

  • Legacy resource loading and mail session setup previously involved java.lang.SecurityManager context checks.
  • Under Java 21, spring-context-support 7.0.9 operates cleanly without SecurityManager references and adheres strictly to modular boundaries (org.springframework.context.support automatic module name) without requiring runtime --add-opens flags.

JVM Command-Line Flag Options (Legacy Workaround)

If forced to run older spring-context-support binaries (< 6.0.0) on Java 21 during transition phases:

# Bypass JPMS encapsulation checks for dynamic mail session setup and reflection-based cache loading
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Spring Framework Baseline 7.0.9 Ensure ${spring.version} is defined as 7.0.9 (or latest Spring 6.1+/7.x release) across parent POMs and dependency management blocks.

Maven Configuration

<properties>
    <!-- Set Spring Framework version to a Java 21 / Virtual Thread safe baseline -->
    <spring.version>7.0.9</spring.version>
</properties>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    springVersion = '7.0.9'
}

dependencies {
    implementation "org.springframework:spring-context-support:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-web

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-web
  • Version: ${spring.version} (analyzed baseline: Spring Framework 5.3.x and 6.0.x)
  • Compile-Time JDK Baseline: Java 8 (major version 52.0) for 5.3.x, Java 17 (major version 61.0) for 6.x and 7.x
  • Java 21 Compatibility Status: Conditional / version-dependent
    • Spring 5.3.x (EOL): Not supported on Java 21. ASM class reading fails on Java 21 bytecode (Unsupported class file major version 65), and the javax.* namespace blocks newer containers.
    • Spring 6.0.x: Runs on Java 17 but lacks complete Java 21 support (ASM and CGLIB class-file handling).
  • Spring 6.1+ / 7.0: Officially supports Java 21, including virtual threads. Spring 7.0 targets Jakarta EE 11.

Key Architectural & Technical Characteristics on Java 21

  • Class-File Parsing (ASM) & Bytecode Level:
    • Component scanning and configuration-class processing read class files with a repackaged ASM.
    • Spring versions older than 6.1 cannot parse Java 21 class files (major version 65), which breaks startup when your own classes are compiled with --release 21.
  • Jakarta Namespace Migration (javax → jakarta):
    • Spring 6.0+ requires jakarta.* APIs. Replace javax.servlet, javax.annotation, javax.validation and javax.persistence imports in your code.
    • Third-party libraries on the classpath must also be Jakarta-ready.
  • Virtual Threads (Project Loom):
    • Spring 6.1+ supports virtual threads, for example SimpleAsyncTaskExecutor.setVirtualThreads(true) and virtual-thread-friendly RestClient.
    • On Java 21, synchronized blocks around blocking I/O still pin carrier threads (fixed in JDK 24+). Audit your own code for this pattern.
  • HTTP Client Modernization:
  • RestTemplate still works but is in maintenance mode. Prefer RestClient (6.1+) or WebClient.

JVM Command-Line Flag Options (Legacy Workaround)

If you are forced to run older Spring 6.0.x on Java 21 during a transition phase (not recommended for production):

# Relax JPMS encapsulation for reflection-heavy proxies
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Spring 5.3.x cannot be fixed with flags. Upgrade it.

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Spring Framework 6.1.x or later (7.0.x for Jakarta EE 11) in the parent POM.

Maven Configuration

<properties>
    <!-- Java 21 / virtual-thread ready baseline -->
    <java.version>21</java.version>
    <spring.version>7.0.0</spring.version>
</properties>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    springVersion = '7.0.0'
}
dependencies {
    implementation "org.springframework:spring-web:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-webmvc

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-webmvc
  • Version: ${spring.version} (must always match spring-web)
  • Compile-Time JDK Baseline: Java 8 (52.0) for 5.3.x, Java 17 (61.0) for 6.x and 7.x
  • Java 21 Compatibility Status: Conditional / version-dependent
    • Spring 5.3.x: Fails or misbehaves on Java 21 and runs only on javax.servlet containers (Tomcat 9).
    • Spring 6.1+: Fully supported on Java 21 with Servlet 6.0 (Tomcat 10.1, Jetty 12).
  • Spring 7.0: Servlet 6.1 / Jakarta EE 11 (Tomcat 11).

Key Architectural & Technical Characteristics on Java 21

  • Servlet Container Alignment:
    • Spring MVC 6+ needs a jakarta.servlet container. Tomcat 9 (javax.servlet) will not start the application.
    • Upgrade to Tomcat 10.1+, Jetty 12 or Undertow (Jakarta build).
  • Virtual Threads for Request Handling:
    • On Tomcat 10.1+ you can run each request on a virtual thread. With Spring Boot 3.2+, set spring.threads.virtual.enabled=true.
    • Avoid long synchronized sections and heavy ThreadLocal caches in controllers, filters and interceptors.
  • Removed and Changed MVC Behavior (6.0+):
    • Trailing-slash matching is off by default (/users no longer matches /users/).
    • PathPatternParser replaces AntPathMatcher as the default.
    • @RequestMapping on interfaces and type-level detection is stricter. Re-test your controller routes.
  • Jackson, Validation and Message Converters:
    • Bean Validation moves to jakarta.validation (Hibernate Validator 8+).
  • Spring 7.0 prefers Jackson 3. Check custom HttpMessageConverter and ObjectMapper customizations.

JVM Command-Line Flag Options (Legacy Workaround)

Useful only while upgrading older 6.0.x applications:

# Diagnose virtual thread pinning during load tests
-Djdk.tracePinnedThreads=full
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Keep spring-web and spring-webmvc on the identical ${spring.version} and upgrade the servlet container in the same release.

Maven Configuration

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

dependencies {
    implementation "org.springframework:spring-webmvc:${springVersion}"
    compileOnly "jakarta.servlet:jakarta.servlet-api:6.1.0"
}
Enter fullscreen mode Exit fullscreen mode

Migration Checklist

  • Upgrade the JDK to 21 and set 21 (or --release 21).
  • Bump ${spring.version} to 6.1.x+ (7.0.x preferred) once, in the parent POM.
  • Replace all javax. imports with jakarta..
  • Upgrade the servlet container (Tomcat 10.1+ / Jetty 12).
  • Run load tests with -Djdk.tracePinnedThreads=full before enabling virtual threads.

Artifact: org.springframework:spring-webmvc

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-webmvc
  • Version: ${spring.version} (target baseline: Spring Framework 7.0.9)
  • Compile-Time JDK Baseline: Java 17 (major version 61.0) for 6.x and 7.x
  • Java 21 Compatibility Status: Fully compatible (7.0.x line)
    • Legacy Spring 5.3.x / 6.0.x: Fail or warn on Java 21 due to ASM class-file parsing limits and the javax.servlet namespace.
  • Spring 7.0.x: Supports Java 17 through 25, virtual threads, Servlet 6.1 and Jakarta EE 11.

Key Architectural & Technical Characteristics on Java 21

  • Servlet Container Alignment:
    • Spring MVC 7.0 requires a jakarta.servlet 6.1 container. Use Tomcat 11 or Jetty 12.1. Tomcat 9 and 10.0 will not start the application.
  • Virtual Threads for Request Handling:
    • Run each request on a virtual thread. With Spring Boot, set spring.threads.virtual.enabled=true.
    • Avoid synchronized blocks around blocking I/O in controllers, filters and interceptors, because they pin carrier threads on Java 21.
  • Behavior Changes Since 6.0:
    • Trailing-slash matching is off by default (/users no longer matches /users/).
    • PathPatternParser is the default matcher instead of AntPathMatcher.
  • Re-test all controller routes after the upgrade.

JVM Command-Line Flag Options (Diagnostics)

No --add-opens flags are required on Spring 7.0.x. Use this flag only to find pinning during load tests:

-Djdk.tracePinnedThreads=full
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Enforce Spring Framework 7.0.9 and upgrade the servlet container in the same release.

Maven Configuration

<properties>
    <maven.compiler.release>21</maven.compiler.release>
    <spring.version>7.0.9</spring.version>
</properties>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    springVersion = '7.0.9'
}
dependencies {
    implementation "org.springframework:spring-webmvc:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-jdbc

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-jdbc
  • Version: ${spring.version} (target baseline: Spring Framework 7.0.9)
  • Compile-Time JDK Baseline: Java 17 (major version 61.0) for 6.x and 7.x
  • Java 21 Compatibility Status: Fully compatible, but dependent on your JDBC driver and connection pool.
    • Spring side: JdbcTemplate, NamedParameterJdbcTemplate and JdbcClient run unchanged on Java 21.
  • Driver side: Old JDBC drivers and pools may use synchronized internally and pin virtual threads.

Key Architectural & Technical Characteristics on Java 21

  • JDBC Driver and Pool Compatibility:
    • Upgrade to current driver releases that officially support Java 21 (PostgreSQL, MySQL Connector/J, Oracle, MS SQL).
    • Use a recent HikariCP. Size the pool deliberately, because virtual threads can create far more concurrent requests than there are connections.
  • Virtual Thread Pinning:
    • Blocking JDBC calls inside synchronized code pin the carrier thread on Java 21.
    • Check driver release notes for virtual-thread fixes and verify with -Djdk.tracePinnedThreads=full.
  • API Modernization:
    • Prefer JdbcClient (fluent API, available since 6.1) for new code. JdbcTemplate remains fully supported.
  • Spring 6.1+ no longer discovers parameter names from debug info, so compile with -parameters (see the build configuration below).

JVM Command-Line Flag Options (Diagnostics)

No --add-opens flags are required. Pinning diagnostics only:

-Djdk.tracePinnedThreads=short
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Keep spring-jdbc on ${spring.version} and upgrade the JDBC driver and connection pool alongside it.

Maven Configuration

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

dependencies {
    implementation "org.springframework:spring-jdbc:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-tx

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-tx
  • Version: ${spring.version} (target baseline: Spring Framework 7.0.9)
  • Compile-Time JDK Baseline: Java 17 (major version 61.0) for 6.x and 7.x
  • Java 21 Compatibility Status: Fully compatible (7.0.x line)
    • Legacy Spring 5.3.x: Transaction proxies and class scanning can fail on Java 21 bytecode.
  • Spring 7.0.x: @Transactional, TransactionTemplate and reactive transactions are supported on Java 21 and Jakarta EE 11.

Key Architectural & Technical Characteristics on Java 21

  • Jakarta Transaction API:
    • Spring 6+ uses jakarta.transaction. Replace javax.transaction.Transactional with jakarta.transaction.Transactional or Spring's own @Transactional.
  • Thread-Bound Transaction Context:
    • Transactions are bound to the current thread. Each virtual thread has its own context, which is safe.
    • Never hand a transactional resource to another thread, and keep transactions short, because they hold a pooled connection.
  • Proxying and Bytecode:
    • Spring 7.0 ships a CGLIB and ASM version that reads and generates Java 21 bytecode. No agent or flag is needed.
  • Do not declare @Transactional methods final or private, because the proxy cannot intercept them.

JVM Command-Line Flag Options (Legacy Workaround)

None required on Spring 7.0.x. Upgrade instead of relying on flags for older versions.

Recommended Build Configuration & Migration Strategy

Action Required: Keep spring-tx aligned with spring-jdbc and every other Spring module.

Maven Configuration

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

dependencies {
    implementation "org.springframework:spring-tx:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: org.springframework:spring-expression

Original JDK Target & Baseline Requirements

  • Group ID: org.springframework
  • Artifact ID: spring-expression
  • Version: ${spring.version} (target baseline: Spring Framework 7.0.9)
  • Compile-Time JDK Baseline: Java 17 (major version 61.0) for 6.x and 7.x
  • Java 21 Compatibility Status: Fully compatible, with reflection caveats.
    • SpEL on application classes: Works unchanged.
  • SpEL on JDK internals: Reflective access to non-exported JDK packages fails under strong encapsulation (InaccessibleObjectException).

Key Architectural & Technical Characteristics on Java 21

  • Strong Encapsulation (JPMS):
    • Expressions that reach into java.base internals, for example T(sun.misc.Unsafe) or private JDK fields, are blocked on Java 21.
    • Rewrite them to use public APIs instead of opening modules.
  • SpEL Compiler Mode:
    • The optional spring.expression.compiler.mode (IMMEDIATE or MIXED) generates bytecode at runtime, and Spring 7.0 supports Java 21 class files.
    • Keep the default OFF unless profiling shows SpEL is a hot spot, then test expressions in both modes.
  • Parameter Name Discovery:
  • Spring 6.1+ requires the -parameters compiler flag. Without it, method and constructor parameter names used by SpEL, MVC and JDBC are not resolved.

JVM Command-Line Flag Options (Legacy Workaround)

Only if legacy expressions touch JDK internals during a transition phase:

# Open only the packages that are actually required
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Compile with -parameters and --release 21, and audit SpEL expressions for JDK-internal access.

Maven Configuration

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <release>21</release>
        <parameters>true</parameters>
    </configuration>
</plugin>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${spring.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

tasks.withType(JavaCompile).configureEach {
    options.release = 21
Enter fullscreen mode Exit fullscreen mode

options.compilerArgs << '-parameters'

}
dependencies {
    implementation "org.springframework:spring-expression:${springVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Migration Checklist

  • Set 7.0.9 once in the parent POM and never override it per module.
  • Compile with --release 21 and -parameters.
  • Replace all javax. imports with jakarta..
  • Move to a Servlet 6.1 container (Tomcat 11 or Jetty 12.1).
  • Upgrade the JDBC driver and connection pool to Java 21 certified releases.
  • Run load tests with -Djdk.tracePinnedThreads=full before enabling virtual threads.

Artifact: com.citi.176352:qfix

Original JDK Target & Baseline Requirements

  • Group ID: com.citi.176352
  • Artifact ID: qfix
  • Version: 1.3_C64 (non-standard qualifier, so Maven version ordering and range resolution may behave unexpectedly)
  • Compile-Time JDK Baseline: Java 11 (major version 55.0), built with JDK 11.0.21
  • Java 21 Compatibility Status: Conditional / unverified.
    • Bytecode level: Java 11 class files load and run on a Java 21 JVM. Bytecode compatibility is not the risk.
    • Runtime risk: Behavior changes between Java 12 and 21 (strong encapsulation, removed APIs, bundled library limits) can break the artifact or its transitive dependencies.
  • Confirmation: Only a test run on Java 21 or a Java 21 build from the owning team can confirm it.

Key Architectural & Technical Characteristics on Java 21

  • Strong Encapsulation (JPMS) Enforced by Default:
    • Java 11 only warned about illegal reflective access. Since Java 17 (--illegal-access removed), access to non-exported JDK packages fails with InaccessibleObjectException.
    • Any code or transitive dependency that reflects into java.base internals (sun.misc.Unsafe, sun.nio.ch, private JDK fields) needs updating or explicit --add-opens.
  • Removed and Deprecated JDK APIs (Java 12 to 21):
    • The Nashorn JavaScript engine is gone (Java 15). Any ScriptEngineManager("nashorn") usage fails.
    • SecurityManager is deprecated for removal and disabled by default, and Thread.stop() and related methods now throw UnsupportedOperationException.
    • Finalization (finalize()) is deprecated for removal, so move resource cleanup to try-with-resources or Cleaner.
  • Bytecode Libraries and Dynamic Agents:
    • ASM, ByteBuddy, CGLIB, Javassist and Mockito older than their Java 21 releases cannot read class files of major version 65.
    • Java 21 prints a warning when an agent is loaded dynamically at runtime (JEP 451). Attach agents at startup with -javaagent instead.
  • Virtual Threads (Project Loom):
    • If the artifact does network or session I/O inside synchronized blocks (common in message and session engines), it pins virtual thread carriers on Java 21.
    • Keep it on platform threads, or replace synchronized with ReentrantLock if you control the source.
  • Behavior Differences to Regression-Test:
    • The default charset is UTF-8 since Java 18 (JEP 400). Encoding-sensitive protocol messages, logs and file I/O may change behavior.
  • Time zone data, Locale data (CLDR) and SimpleDateFormat output differ from Java 11. Re-check any date or time stamping.

JVM Command-Line Flag Options (Legacy Workaround)

If the artifact only runs on Java 21 with reflection into JDK internals, open the minimum required packages during the transition phase:

# Open only the packages that jdeps / the failing stack trace actually name
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
# Restore pre-Java 18 charset behavior if encoding differences appear
-Dfile.encoding=COMPAT
Enter fullscreen mode Exit fullscreen mode

These flags are temporary. Ask the owning team to remove the underlying cause.

Verification Steps (Run Before Migrating)

Check the real bytecode level, internal-API usage and removed-API usage of the jar:

# 1. Confirm the compile target (expect "major version: 55")
Enter fullscreen mode Exit fullscreen mode

javap -v -cp qfix-1.3_C64.jar | grep "major version"

# 2. Find use of JDK internals (run with JDK 21)
Enter fullscreen mode Exit fullscreen mode

jdeps --jdk-internals --multi-release 21 -cp "lib/*" qfix-1.3_C64.jar

# 3. Find use of deprecated-for-removal APIs
Enter fullscreen mode Exit fullscreen mode

jdeprscan --release 21 --for-removal qfix-1.3_C64.jar

# 4. Smoke-test on Java 21 and surface warnings
Enter fullscreen mode Exit fullscreen mode

java -Xlog:disable -Xlog:all=warning:stderr -jar app.jar

Recommended Build Configuration & Migration Strategy

Action Required: Keep qfix pinned to the exact tested version and request a Java 21 certified build from the owning team.

  • Run the verification steps above and record the results.
  • If clean, run the full regression suite on a Java 21 runtime with qfix unchanged.
  • If jdeps or jdeprscan report problems, request a rebuilt qfix compiled and tested with --release 21, and update ${qfix.version}. ### Maven Configuration
<properties>
    <!-- Pin the exact tested internal build. Do not use version ranges with this qualifier. -->
    <qfix.version>1.3_C64</qfix.version>
    <maven.compiler.release>21</maven.compiler.release>
</properties>
<dependency>
    <groupId>com.citi.176352</groupId>
    <artifactId>qfix</artifactId>
    <version>${qfix.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    qfixVersion = '1.3_C64'
}
dependencies {
    implementation "com.citi.176352:qfix:${qfixVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Migration Checklist

  • Confirm the class major version with javap -v (expected 55.0).
  • Run jdeps --jdk-internals and jdeprscan --release 21 --for-removal on the jar and its transitive dependencies.
  • Upgrade all bytecode libraries (ASM, ByteBuddy, CGLIB, Mockito) to Java 21 capable releases.
  • Check UTF-8 default charset effects on protocol and file I/O.
  • Add --add-opens flags only for packages named in actual errors.
  • Ask the owning team for a Java 21 certified build of qfix.

Save changesAI Disclosure

Artifact: com.tibco:tibjms

Original JDK Target & Baseline Requirements

  • Group ID: com.tibco
  • Artifact ID: tibjms
  • Version: ${tibco.version} (analyzed baseline: EMS 8.2.x line, released in the Java 7/8 era)
  • Compile-Time JDK Baseline: Java 7/8 (major version 51.0 / 52.0), to be confirmed with javap
  • Java 21 Compatibility Status: Conditional / vendor-certification dependent.
    • EMS 8.2.x: Predates Java 17 and 21 certification. It may run on Java 21 but is not guaranteed or supported.
  • Current EMS 10.x releases: Certified against newer JDKs. Confirm the exact Java 21 support level in the vendor's supported platforms matrix.

Key Architectural & Technical Characteristics on Java 21

  • javax.jms vs jakarta.jms Namespace:
    • EMS 8.x implements the javax.jms API (JMS 2.0).
    • Spring 6 and 7 (spring-jms) and Jakarta EE 10/11 use jakarta.jms. A javax.jms client cannot be plugged into a Jakarta messaging stack without a bridge or a newer client.
    • Later EMS 10.x releases ship Jakarta Messaging capable client jars. Verify the exact release.
  • Virtual Threads (Project Loom):
    • JMS sessions are single-threaded and blocking. Blocking inside synchronized code pins carrier threads on Java 21.
    • Keep JMS listener containers on platform threads until the vendor documents virtual thread support.
  • Strong Encapsulation (JPMS):
  • Illegal reflective access that Java 11 only warned about fails since Java 17. Run jdeps --jdk-internals on the jar.

JVM Command-Line Flag Options (Legacy Workaround)

If a Java 21 test shows InaccessibleObjectException from the client library, open only the package named in the stack trace:

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Confirm Java 21 support for your exact EMS client version with the vendor, and plan an upgrade to a certified EMS 10.x client.

Maven Configuration

<properties>
    <!-- One version for every com.tibco artifact. Confirm the exact value, 8.2.2l vs 8.2.21 -->
    <tibco.version>8.2.2l</tibco.version>
</properties>
<dependency>
    <groupId>com.tibco</groupId>
    <artifactId>tibjms</artifactId>
    <version>${tibco.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    tibcoVersion = '8.2.2l'
}
dependencies {
    implementation "com.tibco:tibjms:${tibcoVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.tibco:tibcrypt

Original JDK Target & Baseline Requirements

  • Group ID: com.tibco
  • Artifact ID: tibcrypt
  • Version: ${tibco.version} (runtime scope)
  • Compile-Time JDK Baseline: Java 7/8, to be confirmed with javap
  • Java 21 Compatibility Status: Conditional / high attention.
    • Loading and bytecode: Old class files load normally on Java 21.
  • Security behavior: Legacy cryptography settings (old TLS versions, weak ciphers, old key sizes) are blocked by modern JDK security defaults.

Key Architectural & Technical Characteristics on Java 21

  • Disabled Legacy TLS and Algorithms:
    • Java 21 disables TLS 1.0 and 1.1, SSLv3, RC4, 3DES and weak key sizes through jdk.tls.disabledAlgorithms and jdk.certpath.disabledAlgorithms.
    • If your EMS server or truststore uses these, SSL connections fail after the upgrade with SSLHandshakeException or NoSuchAlgorithmException.
  • Keystore and Certificate Formats:
    • Java 18+ defaults to PKCS12. JKS keystores still load, but check any code or config that assumes a keystore type.
    • Re-test certificate chains signed with SHA-1 or small RSA keys.
  • Fix the Server, Not the JVM:
  • Prefer upgrading the EMS server TLS configuration to TLS 1.2/1.3 over relaxing JDK security properties.

JVM Command-Line Flag Options (Legacy Workaround)

Diagnose handshake failures first:

-Djavax.net.debug=ssl:handshake
Enter fullscreen mode Exit fullscreen mode

Only as a short-term, risk-accepted transition measure, a custom java.security override file can relax jdk.tls.disabledAlgorithms:

-Djava.security.properties=/path/to/relaxed-java.security
Enter fullscreen mode Exit fullscreen mode

Do not use this in production without a security review.

Recommended Build Configuration & Migration Strategy

Action Required: Keep tibcrypt on the same version as tibjms and test every TLS connection on Java 21.

Maven Configuration

<dependency>
    <groupId>com.tibco</groupId>
    <artifactId>tibcrypt</artifactId>
    <version>${tibco.version}</version>
    <scope>runtime</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

dependencies {
    runtimeOnly "com.tibco:tibcrypt:${tibcoVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.tibco:tibjmsadmin

Original JDK Target & Baseline Requirements

  • Group ID: com.tibco
  • Artifact ID: tibjmsadmin
  • Version: ${tibco.version}
  • Compile-Time JDK Baseline: Java 7/8, to be confirmed with javap
  • Java 21 Compatibility Status: Conditional / follows tibjms.
  • It depends on tibjms and the EMS server admin protocol, so it inherits every limitation above.

Key Architectural & Technical Characteristics on Java 21

  • Admin API Dependency Chain:
    • Admin calls (create queue, list destinations, server stats) run over the same client connection and TLS stack as tibjms.
  • Scope Hygiene:
    • Administration code rarely belongs in a web application's runtime classpath. Use provided, a tooling module or a separate admin utility to reduce the migration surface.
  • Thread Behavior:
  • Admin calls are blocking. Do not call them from virtual threads inside synchronized blocks.

JVM Command-Line Flag Options (Legacy Workaround)

None specific. Use the --add-opens flags from the tibjms section only if a stack trace names a package.

Recommended Build Configuration & Migration Strategy

Action Required: Keep tibjmsadmin on the identical version as tibjms and remove it from deployable artifacts if unused.

Maven Configuration

<dependency>
    <groupId>com.tibco</groupId>
    <artifactId>tibjmsadmin</artifactId>
    <version>${tibco.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

dependencies {
    implementation "com.tibco:tibjmsadmin:${tibcoVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: com.tibco:tibrvj

Original JDK Target & Baseline Requirements

  • Group ID: com.tibco
  • Artifact ID: tibrvj
  • Version: ${tibco.version}
  • Compile-Time JDK Baseline: Java 7/8, to be confirmed with javap
  • Java 21 Compatibility Status: Conditional / highest risk of the four.
    • tibrvj is the TIBCO Rendezvous Java API, which wraps native (JNI) Rendezvous libraries.
  • Java 21 support depends on the Rendezvous release and its native binaries, not only on the jar.

Key Architectural & Technical Characteristics on Java 21

  • JNI and Native Library Loading:
    • The jar loads native tibrvj libraries through System.loadLibrary. The native binaries must be 64-bit builds matching the OS and the Java 21 JVM.
    • Set java.library.path explicitly. A mismatch fails with UnsatisfiedLinkError.
  • Restricted Native Access (JEP 442/472 direction):
    • Java 21 prints warnings for some native access patterns in newer JDKs. Treat new JNI warnings as a trend to watch.
  • Virtual Threads:
    • Native calls pin virtual threads. Keep Rendezvous event loops on dedicated platform threads.
  • Version Alignment:
  • Keep the Rendezvous daemon, native libraries and tibrvj.jar on the same release.

JVM Command-Line Flag Options (Legacy Workaround)

# Point the JVM at the matching 64-bit native Rendezvous libraries
-Djava.library.path=/opt/tibco/rv/lib
# Diagnose native loading problems
-Xcheck:jni
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Validate Rendezvous native libraries and tibrvj on a Java 21 runtime in a non-production environment before cutover.

Maven Configuration

<dependency>
    <groupId>com.tibco</groupId>
    <artifactId>tibrvj</artifactId>
    <version>${tibco.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

dependencies {
    implementation "com.tibco:tibrvj:${tibcoVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Verification Steps (Run Before Migrating)

# 1. Confirm the compile target of each jar
Enter fullscreen mode Exit fullscreen mode

javap -v -cp tibjms.jar | grep "major version"

# 2. Find use of JDK internals (run with JDK 21)
Enter fullscreen mode Exit fullscreen mode

jdeps --jdk-internals --multi-release 21 -cp "lib/*" tibjms.jar tibcrypt.jar tibjmsadmin.jar tibrvj.jar

# 3. Find deprecated-for-removal API use
Enter fullscreen mode Exit fullscreen mode

jdeprscan --release 21 --for-removal tibjms.jar

# 4. Smoke-test connectivity with TLS debugging
Enter fullscreen mode Exit fullscreen mode

java -Djavax.net.debug=ssl:handshake -jar app.jar

Migration Checklist

  • Resolve the 8.2.21 vs 8.2.2l mismatch and use a single ${tibco.version} property.
  • Ask the vendor for the Java 21 support statement for your exact EMS and Rendezvous releases.
  • Check javax.jms vs jakarta.jms compatibility before adding spring-jms on Spring 7.0.
  • Test every TLS connection against Java 21 security defaults.
  • Validate Rendezvous native libraries (64-bit, matching java.library.path).
  • Keep JMS and Rendezvous threads on platform threads until virtual thread support is confirmed.

Save changesAI DisclosureAdv

Artifacts: Tibco version 8.2.21 summary

Version 8.2.21 is not officially Java 21 certified as far as I know, and I can’t verify it from memory, so confirm with TIBCO support (Cloud Software Group) before relying on it.

  • com.tibco:tibjms 8.2.21: Probably loads and runs basic JMS on Java 21, but it is unsupported by the vendor. It also uses the javax.jms namespace, so it won’t plug into Jakarta (jakarta.jms) stacks such as spring-jms on Spring 7.0.
  • com.tibco:tibcrypt 8.2.21: Highest practical risk for SSL/TLS connections, because Java 21 disables legacy TLS versions and weak ciphers that an old EMS setup may still use.
  • com.tibco:tibjmsadmin 8.2.21: Follows tibjms, so it has the same compatibility status. Remove it from deployable artifacts if the admin API is not used.
  • com.tibco:tibrvj 8.2.21: Highest overall risk, because it wraps native (JNI) Rendezvous libraries. Java 21 support depends on the 64-bit native binaries matching your OS and JVM, not just the jar.

The safest path is to run a Java 21 smoke test (connect, send, receive, and test TLS) and ask the vendor about an upgrade to a Java 21 certified EMS 10.x client.

The earlier block used 8.2.2l, so change it to this:

Artifact: com.tibco:tibrvj

Original JDK Target & Baseline Requirements

  • Group ID: com.tibco
  • Artifact ID: tibrvj
  • Version: 8.2.21 (defined as ${tibco.version}, shared with tibjms, tibcrypt and tibjmsadmin)
  • Compile-Time JDK Baseline: Java 7/8 (major version 51.0 / 52.0), to be confirmed with javap
  • Java 21 Compatibility Status: Conditional / not vendor-certified (highest risk of the TIBCO artifacts).
    • Jar bytecode: Old class files load on a Java 21 JVM.
    • Native layer: The JNI libraries must be 64-bit builds matching your OS and the Java 21 JVM, otherwise loading fails with UnsatisfiedLinkError.
  • Vendor support: Version 8.2.21 predates Java 17/21 certification, so confirm support with TIBCO (Cloud Software Group).

Key Architectural & Technical Characteristics on Java 21

  • JNI and Native Library Loading:
    • The jar loads native Rendezvous libraries through System.loadLibrary. Set java.library.path explicitly to the matching 64-bit directory.
    • Keep the Rendezvous daemon, native libraries and tibrvj jar on the same release.
  • Virtual Threads (Project Loom):
    • Native calls and blocking event-queue dispatch pin virtual thread carriers on Java 21.
    • Run Rendezvous event loops and dispatchers on dedicated platform threads.
  • Strong Encapsulation (JPMS):
    • Illegal reflective access that Java 11 only warned about fails with InaccessibleObjectException since Java 17. Run jdeps --jdk-internals on the jar.
  • Native Access Trend:
  • Newer JDKs print warnings or restrict native access patterns (JEP 442 and later). Treat new JNI warnings as an early signal for future upgrades.

JVM Command-Line Flag Options (Legacy Workaround)

# Point the JVM at the matching 64-bit native Rendezvous libraries
-Djava.library.path=/opt/tibco/rv/lib
# Diagnose native loading and JNI usage problems
-Xcheck:jni
# Only if a stack trace names a JDK package, open that package and nothing else
--add-opens java.base/java.lang=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Recommended Build Configuration & Migration Strategy

Action Required: Pin tibrvj to 8.2.21, validate the native libraries on Java 21 in a non-production environment, and ask the vendor for a Java 21 certified Rendezvous release.

Maven Configuration

<properties>
    <!-- Verify the last character is the digit 1, not a lowercase L -->
    <tibco.version>8.2.21</tibco.version>
</properties>
<dependency>
    <groupId>com.tibco</groupId>
    <artifactId>tibrvj</artifactId>
    <version>${tibco.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    tibcoVersion = '8.2.21'
}
dependencies {
    implementation "com.tibco:tibrvj:${tibcoVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Verification Steps (Run Before Migrating)

# 1. Confirm the compile target of the jar
Enter fullscreen mode Exit fullscreen mode

javap -v -cp tibrvj.jar | grep "major version"

# 2. Find use of JDK internals (run with JDK 21)
Enter fullscreen mode Exit fullscreen mode

jdeps --jdk-internals --multi-release 21 tibrvj.jar

# 3. Find deprecated-for-removal API use
Enter fullscreen mode Exit fullscreen mode

jdeprscan --release 21 --for-removal tibrvj.jar

# 4. Smoke-test native loading on Java 21
Enter fullscreen mode Exit fullscreen mode

java -Xcheck:jni -Djava.library.path=/opt/tibco/rv/lib -jar app.jar

Migration Checklist

  • Confirm the version is 8.2.21 (digit one), not 8.2.2l.
  • Use one ${tibco.version} property for every com.tibco artifact.
  • Verify 64-bit native libraries and java.library.path on the Java 21 host.
  • Keep Rendezvous threads on platform threads until virtual thread support is confirmed.
  • Ask the vendor for the Java 21 support statement for your exact release.

Artifact: javax.transaction:jta

Original JDK Target & Baseline Requirements

  • Group ID: javax.transaction
  • Artifact ID: jta
  • Version: 1.3 (Java Transaction API 1.3)
  • Compile-Time JDK Baseline: Java 7/8 (major version 51.0 / 52.0), to be confirmed with javap
  • Java 21 Compatibility Status: Runs on Java 21, but blocked by the namespace.
    • JVM level: Interfaces and annotations only, so it loads and runs on Java 21 with no JVM flags.
    • Framework level: Spring 7.0.9 spring-tx uses jakarta.transaction. javax.transaction.Transactional is no longer recognized.
  • Packaging issue: javax.transaction:jta is the old Sun/Oracle packaging and is superseded by javax.transaction:javax.transaction-api (1.3) and then by jakarta.transaction:jakarta.transaction-api.

Key Architectural & Technical Characteristics on Java 21

  • Namespace Change (javax.transaction → jakarta.transaction):
    • Spring 6+ and 7.0 look for jakarta.transaction.Transactional and jakarta.transaction.TransactionManager.
    • Code that imports javax.transaction.* still compiles against this jar but is ignored by Spring's transaction infrastructure. This causes silent loss of transactions.
  • Duplicate API Classes:
    • Application servers and other libraries often ship their own javax.transaction classes. Two copies on the classpath cause NoSuchMethodError or ClassCastException.
    • Run mvn dependency:tree and exclude duplicates.
  • Third-Party Library Alignment:
  • Any library that needs javax.transaction (older Hibernate, Atomikos, Bitronix) must be upgraded to its Jakarta build.

JVM Command-Line Flag Options (Legacy Workaround)

No JVM flag is needed or useful. The only fix is a source and dependency change.

Recommended Build Configuration & Migration Strategy

Action Required: Replace javax.transaction:jta:1.3 with jakarta.transaction:jakarta.transaction-api and migrate imports to jakarta.transaction.*.

Maven Configuration

<properties>
    <!-- Jakarta Transactions 2.0.x is the Jakarta EE 9/10 API. Jakarta EE 11 uses 2.0.x as well. Verify against your container. -->
    <jakarta.transaction.version>2.0.1</jakarta.transaction.version>
</properties>
<dependency>
    <groupId>jakarta.transaction</groupId>
    <artifactId>jakarta.transaction-api</artifactId>
    <version>${jakarta.transaction.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jakartaTransactionVersion = '2.0.1'
}
dependencies {
    implementation "jakarta.transaction:jakarta.transaction-api:${jakartaTransactionVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Artifact: javax.jms:javax.jms-api

Original JDK Target & Baseline Requirements

  • Group ID: javax.jms
  • Artifact ID: javax.jms-api
  • Version: 2.0.1 (Java Message Service 2.0 API)
  • Compile-Time JDK Baseline: Java 7 (major version 51.0), to be confirmed with javap
  • Java 21 Compatibility Status: Runs on Java 21, but blocked by the namespace.
    • JVM level: Interfaces only, so it loads and runs on Java 21.
  • Framework level: spring-jms 7.0.9 uses jakarta.jms. A javax.jms provider (such as the TIBCO EMS 8.2.21 tibjms client) cannot be plugged into it directly.

Key Architectural & Technical Characteristics on Java 21

  • Namespace Change (javax.jms → jakarta.jms):
    • Jakarta Messaging 3.x uses jakarta.jms.*. Spring 6 and 7 JmsTemplate, @JmsListener and DefaultMessageListenerContainer need jakarta.jms.
    • The API jar and the provider client must use the same namespace. javax.jms-api 2.0.1 pairs only with a javax.jms provider.
  • Link to the TIBCO Artifacts:
    • tibjms 8.2.21 implements javax.jms. Staying on this jar keeps the stack on javax.jms, which means Spring 7.0.9 spring-jms cannot be used with it.
    • Options: upgrade to an EMS client that ships Jakarta Messaging, use a bridge, or isolate JMS code behind a small adapter.
  • Virtual Threads (Project Loom):
  • JMS sessions are single-threaded and blocking. Keep listener containers on platform threads until the provider documents virtual thread support.

JVM Command-Line Flag Options (Legacy Workaround)

No JVM flag is needed or useful. The only fix is a source and dependency change.

Recommended Build Configuration & Migration Strategy

Action Required: Decide the namespace per module. Move to jakarta.jms only when the JMS provider client supports it. Keep javax.jms-api 2.0.1 only in modules that remain on the legacy provider.

Maven Configuration

<properties>
    <!-- Jakarta Messaging 3.1.0 matches Jakarta EE 10. Verify against your provider. -->
    <jakarta.jms.version>3.1.0</jakarta.jms.version>
</properties>
<dependency>
    <groupId>jakarta.jms</groupId>
    <artifactId>jakarta.jms-api</artifactId>
    <version>${jakarta.jms.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    jakartaJmsVersion = '3.1.0'
}
dependencies {
    implementation "jakarta.jms:jakarta.jms-api:${jakartaJmsVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Verification Steps (Run Before Migrating)

# 1. Find every javax.transaction / javax.jms usage in source
Enter fullscreen mode Exit fullscreen mode

grep -rn "import javax.transaction|import javax.jms" src/

# 2. Find duplicate API jars on the classpath
Enter fullscreen mode Exit fullscreen mode

mvn dependency:tree | grep -i "javax.transaction|javax.jms|jakarta.transaction|jakarta.jms"

# 3. Confirm the compile target of the jars
Enter fullscreen mode Exit fullscreen mode

javap -v -cp jta-1.3.jar javax.transaction.Transactional | grep "major version"

# 4. Find JDK internals use (run with JDK 21)
Enter fullscreen mode Exit fullscreen mode

jdeps --jdk-internals --multi-release 21 -cp "lib/*" jta-1.3.jar javax.jms-api-2.0.1.jar

Migration Checklist

  • Do not keep javax.transaction and jakarta.transaction in the same module.
  • Replace javax.transaction:jta:1.3 with the Jakarta Transactions API.
  • Keep javax.jms-api:2.0.1 only while the TIBCO tibjms 8.2.21 client is in use.
  • Check that spring-jms 7.0.9 and the JMS provider use the same namespace.
  • Exclude duplicate API jars with mvn dependency:tree.
  • Run transaction rollback and JMS send/receive tests on Java 21.

Save changesAI Disclosure

Artifact: org.quartz-scheduler:quartz:2.3.2 on Java 21

Original JDK Target & Baseline Requirements

  • Group ID: org.quartz-scheduler
  • Artifact ID: quartz
  • Version analyzed: 2.3.2 (Quartz 2.3.x line, the version found in the project POM)
  • Compile-Time JDK Baseline: Java 7 (Major Version 51.0) for 2.3.x releases.
  • Namespace Baseline: javax. (for example javax.transaction.UserTransaction in JobStoreCMT). No jakarta. support in 2.3.x.
  • Java 21 Compatibility Status: Conditional / Works with caveats.
    • Quartz 2.3.2 (analyzed): The core scheduler generally runs on Java 21, as it does not use SecurityManager or sun.misc.Unsafe. It is old (2019), however, and depends on aging transitive libraries, so it is not recommended as a long-term Java 21 baseline.
  • Modern Quartz 2.5.x: Newer baseline, actively maintained, and the line aligned with current Spring Framework releases. Use it as the migration target.

Key Architectural & Technical Characteristics on Java 21

1. Aging Transitive Dependencies:

  • Quartz 2.3.2 pulls in c3p0 (connection pool), HikariCP-java7 and an old slf4j-api 1.7.x.
  • The HikariCP-java7 build is a legacy Java 7 variant. Prefer a current HikariCP (Java 11+ build) managed by your application.
  • Run your dependency scanner (OWASP Dependency-Check, Dependabot, Snyk) against 2.3.2. Quartz 2.3.2 and its transitive libraries have had published security findings (for example CVE-2023-39017), so verify the current status. 2. javax vs jakarta Namespace (Migration Blocker for Jakarta EE 10+ Stacks):
  • Quartz 2.3.2 JobStoreCMT and JTA integration use javax.transaction.*.
  • On Spring Boot 3.x / Spring Framework 6.x (Jakarta EE 10), the jakarta.transaction. API is on the classpath, not javax.transaction..
  • Spring's SchedulerFactoryBean with a plain JDBC or RAM job store works. Container-managed JTA with Quartz 2.3.2 needs verification.

3. Virtual Threads (Project Loom):

  • Quartz uses SimpleThreadPool, a pool of platform worker threads, which is not Virtual Thread aware.
  • Jobs that block on I/O will hold a platform thread. Do not assume a Java 21 upgrade changes scheduling throughput.
  • Where needed, plug in a custom org.quartz.spi.ThreadPool or hand heavy work off to a virtual-thread executor inside the job.
  • Avoid long synchronized blocks inside job classes, as they can pin carrier threads on Java 21.

4. Java Serialization and JDBC Job Store:

  • With the default JDBC job store, JobDataMap contents are Java-serialized into BLOB columns.
  • Upgrading Quartz or the JDK can break deserialization of persisted triggers and jobs if class definitions changed.
  • Recommendation: set org.quartz.jobStore.useProperties=true so job data is stored as strings, and test existing persisted data before cutover.

5. Spring Framework 7.0 / Jakarta EE 11 Alignment:

  • Spring Framework 6.x supports Quartz 2.3.x, so staying on 2.3.2 is workable for Spring Boot 3.x on Java 21.
  • Spring Framework 7.0 targets a newer Quartz baseline (2.5.x). Plan the upgrade before adopting Spring 7.0.

JVM Command-Line Flag Options

No --add-opens flags are known to be required for Quartz 2.3.2 itself on Java 21.

Only if your own jobs or third-party libraries use deep reflection into JDK internals:

# Last resort only: open specific JDK packages for unnamed-module code
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Prefer fixing or upgrading the offending library over keeping these flags.

Recommended Build Configuration & Migration Strategy

Action Required: Upgrade Quartz from 2.3.2 to the latest 2.5.x release.

Test the upgrade on a Java 21 JVM against a copy of your production Quartz tables (QRTZ_*) before production rollout. Check the Quartz release notes for schema and javax/jakarta changes.

Maven Configuration

<properties>
    <java.version>21</java.version>
    <!-- Was 2.3.2. Use the latest 2.5.x release -->
    <quartz.version>2.5.0</quartz.version>
</properties>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    quartzVersion = '2.5.0' // was 2.3.2, use the latest 2.5.x release
}
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
dependencies {
    implementation "org.quartz-scheduler:quartz:${quartzVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Quartz Configuration Hardening (quartz.properties)

# Store job data as strings instead of serialized Java objects
org.quartz.jobStore.useProperties=true
# Disable the external update check on startup
org.quartz.scheduler.skipUpdateCheck=true
Enter fullscreen mode Exit fullscreen mode

Pre-Production Migration Checklist

  • Replace quartz:2.3.2 with the latest 2.5.x release in parent POMs and dependency management blocks.
  • Exclude the transitive HikariCP-java7 and c3p0 if you manage your own connection pool.
  • Review javax.transaction usage if you use JobStoreCMT or JTA.
  • Test deserialization of persisted JobDataMap data from existing QRTZ_* tables.
  • Run all scheduled jobs on a Java 21 JVM in UAT, including misfire and clustered-node scenarios.
  • Re-scan dependencies for CVEs after the upgrade.

Artifact: org.apache.velocity:velocity:1.7 on Java 21

Original JDK Target & Baseline Requirements

  • Group ID: org.apache.velocity
  • Artifact ID: velocity (the legacy Velocity 1.x artifact, now superseded by velocity-engine-core)
  • Version analyzed: 1.7 (released 2010, the version found in the project POM, with commons-collections excluded)
  • Compile-Time JDK Baseline: Java 1.4 / 1.5 era (Major Version 48.0 to 49.0) for the 1.7 release.
  • Namespace Baseline: No javax. or jakarta. dependency in the core engine. The optional servlet-related classes live in the separate velocity-tools project.
  • Java 21 Compatibility Status: Conditional / Not Recommended.
    • Velocity 1.7 (analyzed): The engine often still runs on Java 21 for simple templates, but it is end-of-life, unmaintained, and relies on reflection patterns and libraries that conflict with modern JDK restrictions. Your commons-collections exclusion is a runtime risk, covered below.
  • Modern Velocity Engine 2.3.x / 2.4.x (velocity-engine-core): Maintained, uses generics, and is the supported baseline for modern JDKs. Use it as the migration target.

Key Architectural & Technical Characteristics on Java 21

1. The commons-collections Exclusion (Critical for 1.7):

  • Velocity 1.7 declares commons-collections 3.2.1 as a dependency and uses classes from it at runtime (for example in its ExtendedProperties configuration handling).
  • Excluding it in the POM only works if no Velocity code path touches those classes. A missing class shows up as NoClassDefFoundError at runtime, often only when a specific template or configuration path executes.
  • Recommendation: remove the exclusion and use a current, patched commons-collections 3.2.2 (or migrate to Velocity 2.x, which no longer needs it) before testing on Java 21.

2. Reflection-Based Introspection (Strong Encapsulation):

  • Velocity resolves $object.property and $object.method() through reflection (Introspector, ClassMap).
  • On Java 21, strong encapsulation applies to JDK internal classes. Templates that call methods on JDK types (for example $list.class, or non-public JDK implementation classes) can throw InaccessibleObjectException or silently resolve nothing.
  • Velocity 1.7 predates Uberspect hardening and has no built-in protection against template access to dangerous methods such as getClass().
  • Recommendation: allow only the model objects your application owns, and test each template against Java 21.

3. Security Posture of an End-of-Life Engine:

  • Velocity 1.7 has known security findings, including template-injection and class-access issues (for example CVE-2020-13936 affects the 1.x and 2.x lines before the 2.3 fix), so verify the status with your dependency scanner.
  • Never render templates built from untrusted user input with 1.7.
  • Velocity 2.3 and later added the SecureUberspector option to restrict reflective access.

4. Virtual Threads (Project Loom):

  • Velocity 1.7 uses synchronized blocks and shared caches inside RuntimeInstance, the resource manager and the template cache.
  • Under heavy template rendering on virtual threads, these can pin carrier threads on Java 21.
  • Recommendation: use a single shared, pre-initialized VelocityEngine, and prefer 2.x, which has reduced locking on the rendering path.

5. API and Package Changes When Moving to 2.x:

  • The artifact changes from org.apache.velocity:velocity to org.apache.velocity:velocity-engine-core.
  • The org.apache.velocity.runtime.log.* logger bindings changed. Velocity 2.x logs through SLF4J, so remove any custom LogChute implementations.
  • Some directives, ExtendedProperties usage and VelocityContext behaviors differ. Review the official Velocity 2.x upgrade guide.

6. Spring Framework 7.0 / Jakarta EE 11 Alignment:

  • Spring removed its Velocity integration (VelocityConfigurer, VelocityViewResolver) in Spring Framework 4.3 and later.
  • Applications still on Velocity must call the engine directly or use a library such as velocity-engine-spring, so there is no Spring 7.0 compatibility to rely on.

JVM Command-Line Flag Options

No --add-opens flags are known to be required for the Velocity 1.7 core engine itself on Java 21.

Only if your templates reflectively reach into JDK internals:

# Last resort only: open specific JDK packages for unnamed-module code
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
Enter fullscreen mode Exit fullscreen mode

Prefer fixing the template or model object over keeping these flags.

Recommended Build Configuration & Migration Strategy

Action Required: Replace org.apache.velocity:velocity:1.7 with org.apache.velocity:velocity-engine-core 2.3.x or later.

Remove the commons-collections exclusion unless you have proven it is safe. Velocity 2.x does not need it, so the exclusion becomes unnecessary after the upgrade.

Maven Configuration

<properties>
    <java.version>21</java.version>
    <!-- Was velocity:1.7. Use velocity-engine-core 2.3+ (latest 2.x recommended) -->
    <velocity.version>2.3</velocity.version>
</properties>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>${velocity.version}</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Configuration

ext {
    velocityVersion = '2.3' // was velocity:1.7, use the latest 2.x release
}
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
dependencies {
    implementation "org.apache.velocity:velocity-engine-core:${velocityVersion}"
}
Enter fullscreen mode Exit fullscreen mode

Engine Configuration Hardening (velocity.properties)

# Restrict reflective access from templates (Velocity 2.3+)
runtime.introspector.uberspect=org.apache.velocity.util.introspection.SecureUberspector
# Fail fast on missing references
runtime.strict_mode.enable=true
Enter fullscreen mode Exit fullscreen mode

Pre-Production Migration Checklist

  • Change the artifact to velocity-engine-core and the version to the latest 2.x release in parent POMs and dependency management blocks.
  • Remove the commons-collections exclusion once on Velocity 2.x.
  • Replace custom LogChute logging code with SLF4J-based logging.
  • Run every template through a regression suite on a Java 21 JVM, and compare rendered output with the 1.7 output.
  • Review templates for calls on JDK types, $obj.class access and whitespace behavior changes between 1.7 and 2.x.
  • Use a single shared VelocityEngine instance, and test under load with virtual threads if you enable them.
  • Re-scan dependencies for CVEs after the upgrade.

Top comments (0)