Skip to content

fix: add Java 23+/24+ compatibility JVM args and upgrade commons-lang3 to 3.20.0#15417

Merged
jdaugherty merged 5 commits into7.0.xfrom
fix/java-compat-jvm-args
Feb 21, 2026
Merged

fix: add Java 23+/24+ compatibility JVM args and upgrade commons-lang3 to 3.20.0#15417
jdaugherty merged 5 commits into7.0.xfrom
fix/java-compat-jvm-args

Conversation

@jamesfredley
Copy link
Contributor

@jamesfredley jamesfredley commented Feb 19, 2026

Summary

  • Add version-conditional JVM arguments in GrailsGradlePlugin to suppress warnings on Java 23+ and 24+
  • Override commons-lang3 from 3.17.0 (Spring Boot managed) to 3.20.0 in the Grails BOM
  • Include Gradle TestKit functional tests for the JVM arg configuration

Changes

1. Java compatibility JVM args in GrailsGradlePlugin (#15216, #15343)

Adds configureJavaCompatibilityArgs(Project) which conditionally applies JVM flags to all Test and JavaExec tasks based on the target Java version (toolchain-aware, falls back to current JVM):

Flag Java Version Why
--sun-misc-unsafe-memory-access=allow 23+ JEP 471/498 - Netty 4.1.x uses sun.misc.Unsafe.allocateMemory for off-heap buffers. Terminal deprecation warnings appear on 23+. Suppresses until Netty migrates to MemorySegment (Netty 4.2+).
--enable-native-access=ALL-UNNAMED 24+ JEP 472 - hawtjni (JLine 2.x) and Netty call System.loadLibrary/native methods. Warning-only now, becomes mandatory deny in a future JDK.

Flags are not added on Java 17-22, avoiding Unrecognized option errors.

Implementation approach: Uses project.plugins.withId('java') with lazy configureEach callbacks so toolchain configuration is read after build script evaluation. Each flag logs at INFO level when added (e.g. Grails: adding --enable-native-access=ALL-UNNAMED to test for Java 25 compatibility).

Note on #15343: The original issue suggested --add-opens=java.base/sun.misc=ALL-UNNAMED, but that flag addresses module visibility, not the sun.misc.Unsafe memory-access deprecation. The correct flag is --sun-misc-unsafe-memory-access=allow.

2. commons-lang3 BOM override to 3.20.0 (#15045)

Spring Boot 3.5.x manages commons-lang3 at 3.17.0, which has two issues:

  • LANG-1786: FastDatePrinter uses deprecated three-letter timezone IDs (e.g. PST, CST, EST), causing a flood of WARNING: Use of the three-letter time zone ID "XYZ" is deprecated messages on Java 25
  • CVE-2025-48924: StackOverflowError in ClassUtils.getClass with crafted input (fixed in 3.18.0+)

Non-breaking changes analysis (3.17.0 -> 3.20.0)

All versions in this range require Java 8+ (unchanged baseline). There are no breaking changes:

Category Details
Removed methods None
Changed signatures None
Changed behavior LocaleUtils.toLocale() now accepts 2-letter country codes it previously rejected (more lenient, not breaking)
New deprecations Several methods deprecated in favor of newer alternatives - existing calls continue to work
New methods Additions to StringUtils, ArrayUtils, ClassUtils, etc. - purely additive
Bug fixes LANG-1786 (timezone), LANG-1770 (NumberUtils), LANG-1754 (DurationUtils), among others
Security fixes CVE-2025-48924 (ClassUtils StackOverflow)

3. Tests

New GrailsGradlePluginJavaCompatSpec with 4 Gradle TestKit functional tests:

Test Toolchain Verifies
No toolchain (current JDK) None Args match current JDK version
Toolchain = current JDK Dynamic Args match current JDK version
Toolchain = 23 23 Only --sun-misc-unsafe-memory-access=allow
Toolchain = 24 24 Both flags on both Test and JavaExec tasks

Files Changed

  • grails-gradle/plugins/.../GrailsGradlePlugin.groovy - Added configureJavaCompatibilityArgs(), applyCompatArgs(), resolveTargetJavaVersion(), new imports
  • dependencies.gradle - Added commons-lang3.version: 3.20.0 and commons-lang3 BOM entry
  • grails-gradle/plugins/.../GrailsGradlePluginJavaCompatSpec.groovy - New test spec
  • grails-gradle/plugins/.../test-projects/java-compat-* - 4 new test fixture projects

Related Issues

…3 to 3.20.0

Add version-conditional JVM arguments in GrailsGradlePlugin to suppress
warnings on modern JDKs:
- --sun-misc-unsafe-memory-access=allow for Java 23+ (JEP 471/498, #15343)
- --enable-native-access=ALL-UNNAMED for Java 24+ (JEP 472, #15216)

Override commons-lang3 from 3.17.0 (Spring Boot managed) to 3.20.0 in the
Grails BOM to fix LANG-1786 timezone warnings and CVE-2025-48924.

Includes Gradle TestKit functional tests verifying the args are applied
correctly based on toolchain version.

Assisted-by: Claude Code <Claude@Claude.ai>
@github-actions github-actions bot added the bug label Feb 19, 2026
Tests were hardcoding expected values for JDK 17 but CI also runs on
Java 25 where both compat flags are correctly applied. Compute expected
values dynamically from CURRENT_JDK.

Assisted-by: Claude Code <Claude@Claude.ai>
@jamesfredley jamesfredley self-assigned this Feb 19, 2026
@jamesfredley jamesfredley marked this pull request as ready for review February 20, 2026 16:44
Copilot AI review requested due to automatic review settings February 20, 2026 16:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds Java 23+ and 24+ compatibility by configuring JVM arguments to suppress warnings from deprecated APIs and native access restrictions. It also upgrades commons-lang3 from 3.17.0 to 3.20.0 to fix timezone deprecation warnings and a security vulnerability.

Changes:

  • Added version-conditional JVM argument configuration in GrailsGradlePlugin to suppress Java 23+ sun.misc.Unsafe warnings and Java 24+ native access warnings
  • Upgraded commons-lang3 to 3.20.0 in the Grails BOM to fix LANG-1786 (timezone ID deprecation) and CVE-2025-48924
  • Comprehensive Gradle TestKit functional tests for JVM argument configuration with multiple toolchain scenarios

Reviewed changes

Copilot reviewed 15 out of 19 changed files in this pull request and generated no comments.

File Description
grails-gradle/plugins/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy Added configureJavaCompatibilityArgs() method to conditionally apply JVM flags based on target Java version, plus helper method resolveTargetJavaVersion() and new imports
dependencies.gradle Added commons-lang3 3.20.0 version override in BOM to supersede Spring Boot's managed 3.17.0
grails-gradle/plugins/src/test/groovy/org/grails/gradle/plugin/core/GrailsGradlePluginJavaCompatSpec.groovy New test spec with 4 test cases covering no-toolchain, current-toolchain, Java 23, and Java 24 scenarios
grails-gradle/plugins/src/test/resources/test-projects/java-compat-* Four test fixture projects for functional testing of JVM arg configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…r review

Address review feedback from jdaugherty:
- Replace afterEvaluate anti-pattern with project.plugins.withId('java')
- Move version resolution into configureEach callbacks so toolchain
  config is read lazily after build script evaluation
- Add project.logger.info for each JVM compatibility flag added

Assisted-by: Claude Code <Claude@Claude.ai>
…ainForForkTasks

Use plugins.withId('java') with configureEach instead of
afterEvaluate, matching the pattern used by
configureJavaCompatibilityArgs. The configureEach closure defers
execution until task realization (after evaluation), so the
toolchain check works without afterEvaluate.

Assisted-by: Claude Code <Claude@Claude.ai>
@jdaugherty jdaugherty merged commit 418cde8 into 7.0.x Feb 21, 2026
32 checks passed
@jdaugherty jdaugherty deleted the fix/java-compat-jvm-args branch February 21, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment