Native-access via bootJar manifest, not CLI flags everywhere

JDK 22+ honors an 'Enable-Native-Access' attribute on the executable
jar's manifest (JEP 472). Setting it once on the Spring Boot bootJar
removes the need to remember --enable-native-access=ALL-UNNAMED in:
  - Docker init script's JAVA_BASE_OPTS injection
  - Tauri Rust launcher's java_options vector
  - Anywhere else somebody runs the bootJar
…and also future-proofs against JDK 26's hard-fail behavior without
each launch point having to track the flag.

app/core/build.gradle: add 'Enable-Native-Access': 'ALL-UNNAMED' to the
bootJar manifest attributes block.

build.gradle (bootRun jvmArgs): keep --enable-native-access=ALL-UNNAMED
because bootRun launches from classfiles, not the bootJar — the
manifest mechanism doesn't apply to that codepath, only to 'java -jar'.

scripts/init-without-ocr.sh: drop the FFM injection.
frontend/src-tauri/src/commands/backend.rs: drop the CLI arg.
This commit is contained in:
Anthony Stirling
2026-05-18 21:35:06 +01:00
parent 96920b1186
commit 7f8f09c899
4 changed files with 17 additions and 18 deletions
+9 -1
View File
@@ -160,7 +160,15 @@ bootJar {
manifest {
attributes(
'Implementation-Title': 'Stirling-PDF',
'Implementation-Version': project.version
'Implementation-Version': project.version,
// JDK 22+ honors this manifest attribute on the executable jar:
// grant native-access (FFM restricted methods) without needing
// --enable-native-access on the CLI / JAVA_TOOL_OPTIONS at launch.
// ALL-UNNAMED matches the unnamed module that Spring Boot's fat
// jar runs as (everything on the classpath), which includes
// JPDFium and its FFM-using internals.
// Ref: https://openjdk.org/jeps/472 ('Enable-Native-Access' attr)
'Enable-Native-Access': 'ALL-UNNAMED'
)
}
}
+4 -2
View File
@@ -452,8 +452,10 @@ subprojects {
"-XX:+ExplicitGCInvokesConcurrent",
"-XX:+UseStringDeduplication",
"-XX:+UseCompactObjectHeaders",
// JPDFium uses FFM; JDK 26 will reject native access without
// this flag. Required at JDK 25 to silence the warning chain.
// bootRun launches from classfiles (not the bootJar), so the
// 'Enable-Native-Access' manifest attribute baked into the
// jar (see app/core/build.gradle) doesn't apply here.
// Keep the CLI form for the dev loop.
"--enable-native-access=ALL-UNNAMED"
]
}
+4 -5
View File
@@ -205,11 +205,10 @@ fn run_stirling_pdf_jar(app: &tauri::AppHandle, java_path: &PathBuf, jar_path: &
let java_options = vec![
"-Xmx2g",
// JPDFium uses Foreign Function & Memory (FFM). JDK 25 warns without
// this flag; JDK 26+ will refuse native access entirely. ALL-UNNAMED
// because the Spring Boot fat jar runs from the classpath, not the
// module path.
"--enable-native-access=ALL-UNNAMED",
// FFM native access (for JPDFium) is granted by the
// 'Enable-Native-Access: ALL-UNNAMED' manifest attribute baked into
// the Spring Boot bootJar (see app/core/build.gradle). No CLI flag
// needed here.
"-DBROWSER_OPEN=false",
"-DSTIRLING_PDF_TAURI_MODE=true",
&log_path_option,
-10
View File
@@ -816,16 +816,6 @@ if [ "$AOT_ENABLED" = "true" ]; then
fi
fi
# ---------- FFM Native Access ----------
# JPDFium uses the Foreign Function & Memory API to call into PDFium.
# JDK 25 warns when libraries call restricted methods without explicit
# permission; JDK 26+ will refuse outright. ALL-UNNAMED because Stirling-PDF
# runs as a Spring Boot fat jar on the classpath (not the module path).
case "${JAVA_BASE_OPTS}" in
*enable-native-access*) ;;
*) JAVA_BASE_OPTS="${JAVA_BASE_OPTS} --enable-native-access=ALL-UNNAMED" ;;
esac
# Collapse duplicate whitespace
JAVA_BASE_OPTS=$(echo "$JAVA_BASE_OPTS" | tr -s ' ')