Skip to content

feat(loader): bundle native library inside the JAR#77

Open
andygrove wants to merge 1 commit into
apache:mainfrom
andygrove:fat-jar-loader
Open

feat(loader): bundle native library inside the JAR#77
andygrove wants to merge 1 commit into
apache:mainfrom
andygrove:fat-jar-loader

Conversation

@andygrove
Copy link
Copy Markdown
Member

Which issue does this PR close?

Rationale for this change

NativeLibraryLoader currently calls System.loadLibrary("datafusion_jni"), so consumers need to set java.library.path or install the .so/.dylib system-wide before importing the library. That's a barrier to publishing a Maven-Central artifact that just works.

This PR adds the runtime piece of the fat-JAR design from #33: the loader can pull a platform-specific native library out of the JAR's resource tree, and the Maven build copies the locally-built datafusion_jni into that tree before packaging.

What changes are included in this PR?

NativeLibraryLoader:

  • Tries System.loadLibrary first so users can still override the bundled library via java.library.path / LD_LIBRARY_PATH.
  • On UnsatisfiedLinkError, detects the host OS/arch, looks up org/apache/datafusion/<os>/<arch>/lib<name>.<ext> on the classpath, extracts it to $TMPDIR/datafusion-java/<sha256>/, and loads via System.load. Hash-based directory naming lets concurrent JVMs sharing a temp directory converge on the same file; extraction uses ATOMIC_MOVE to avoid racing writers clobbering each other.
  • OS/arch detection lives in a new package-private Platform class that maps the OS/arch combinations from the design in Publish fat JAR with platform-specific native libraries to Maven Central #33: linux/amd64, linux/aarch64, darwin/x86_64, darwin/aarch64 (with a stub for windows/.dll to make the future Windows port a build-matrix change).

Maven (core/pom.xml):

  • Per-platform profiles activate by host OS/arch and set datafusion.lib.os / datafusion.lib.arch / datafusion.lib.filename.
  • A new maven-antrun-plugin execution at process-classes copies native/target/<profile>/lib<name>.<ext> into target/classes/org/apache/datafusion/<os>/<arch>/lib<name>.<ext>, failing fast with a clear message if the native crate hasn't been built. -Ddatafusion.native.profile=release switches the source from debug to release.
  • Drops -Djava.library.path=... from the surefire argline (and the equivalent argument from the examples module's exec-maven-plugin) so the test path now exercises the same resource-extraction code path users will hit. --add-opens=java.base/java.nio=ALL-UNNAMED stays.

Are these changes tested?

  • New PlatformTest covers OS detection (linux/darwin/windows), arch detection across amd64/x86_64/x64/aarch64/arm64 aliases, unsupported-platform error handling, library filename mapping per OS, and resource paths matching the spec in Publish fat JAR with platform-specific native libraries to Maven Central #33 exactly.
  • Existing JNI-using integration tests now load the library via the resource-extraction path (since -Djava.library.path is gone). Full local mvn test is green on macOS aarch64 (229 tests, 13 skipped, 0 failures). Verified that the JAR contains org/apache/datafusion/darwin/aarch64/libdatafusion_jni.dylib and that the extracted copy lands under $TMPDIR/datafusion-java/<sha256>/.

Other host platforms (linux/amd64, linux/aarch64, darwin/x86_64) will be exercised once the cross-build CI matrix lands.

Are there any user-facing changes?

The NativeLibraryLoader.loadLibrary() public API is unchanged. Behavior is strictly broader — code that previously needed java.library.path set still works, and code that depended only on the bundled lib now works too.

Rework NativeLibraryLoader so it can load datafusion_jni from a
JAR-bundled native library instead of requiring java.library.path:

  1. Try System.loadLibrary so operators can still override with a
     system-installed build via java.library.path / LD_LIBRARY_PATH.
  2. On UnsatisfiedLinkError, detect the host OS/arch, look up the
     bundled resource at
     org/apache/datafusion/<os>/<arch>/lib<name>.<ext>, extract it to
     $TMPDIR/datafusion-java/<sha256>/ and load via System.load.

Concurrent JVMs sharing the temp directory converge on the same SHA-256
hash directory; the extraction uses ATOMIC_MOVE so racing writers don't
clobber each other.

Wire core/pom.xml to copy the host's locally built native lib from
native/target/<profile>/lib<name>.<ext> into target/classes at the
matching resource path, so the produced JAR works out of the box on the
build host. Per-platform Maven profiles set the OS/arch directory
segments and library filename; -Ddatafusion.native.profile=release
switches the copy source from debug to release.

Drop -Djava.library.path from the surefire and exec-maven-plugin
argLines so the test path now exercises the same resource-extraction
code path users will hit.

Refs apache#33 (work items 1 and 3); cross-build CI, Sonatype publishing, and
release docs remain as follow-ups.
@andygrove
Copy link
Copy Markdown
Member Author

@pgwhalen fyi

Comment thread core/pom.xml
Comment on lines +99 to +103
<property name="datafusion.native.lib.source"
value="${maven.multiModuleProjectDirectory}/native/target/${datafusion.native.profile}/${datafusion.lib.filename}"/>
<fail message="Native library not found at ${datafusion.native.lib.source}. Run 'cd native &amp;&amp; cargo build' (or 'make') before building the JAR.">
<condition><not><available file="${datafusion.native.lib.source}"/></not></condition>
</fail>
Copy link
Copy Markdown
Contributor

@LantaoJin LantaoJin May 21, 2026

Choose a reason for hiding this comment

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

On Windows, none of the new native profiles defines datafusion.lib.filename, datafusion.lib.os, or datafusion.lib.arch, so this path remains unresolved and the new process-classes fail check aborts mvn test/package even after cargo build creates native/target/debug/datafusion_jni.dll. This regresses the previous java.library.path flow before NativeLibraryLoader can fall back to System.loadLibrary. One fix is adding a Windows profile mirroring the Linux/Mac ones: <datafusion.lib.os>windows</datafusion.lib.os>, <datafusion.lib.arch>amd64</datafusion.lib.arch>, <datafusion.lib.filename>datafusion_jni.dll</datafusion.lib.filename>. Even though no Windows binary will be bundled today, the build at least stops failing, or skipping the copy step on unsupported platforms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants