Call from Java code into C code using the jextract
tool.
jextract is a tool which mechanically generates Java bindings from a native library headers.
I want a Java program that can call into a native library written in C. I've wanted to do this for a long time and jextract
has had a few years to mature so now is a good time to try it out.
Apache Lucene is using Java's exciting
Foreign Function & Memory API which is a preview feature in Java 21. This is cool.
I want to use jextract
to generate Java bindings for a native library and then use the Foreign Function API to call
the library from Java code.
Follow these instructions to build the C library, the Java program and run the Java program.
- Pre-requisite: Java 21
- The source code is authored for Java 21 and must be compiled with Java 21. It's up to you if you want to run Gradle on Java 21 or earlier. Gradle does not technically endorse support for running on Java 21 yet. See the "Support for running Gradle" section in the Gradle Compatibility Matrix.
- Use a matching
jextract
distribution - Compile the C library
-
clang -dynamiclib -o lucky_number.dylib lucky_number.c
-
- Extract the Java bindings
-
jextract --source \ --output src/main/java \ --target-package dgroomes.bindings \ -I "$PWD" \ -l "$PWD/lucky_number.dylib" \ lucky_number.h
- Glance over the generated code. Specifically, notice how the
RuntimeHelper
class hard codes to the fully qualified path of the dynamic shared library to load it. -
final class RuntimeHelper { // ... omitted ... static { System.load("/Users/dave/repos/personal/java-foreign-function-and-memory-api-playground/jextract/lucky_number.dylib"); SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); SYMBOL_LOOKUP = name -> loaderLookup.find(name).or(() -> LINKER.defaultLookup().find(name)); } // ... omitted ... }
- The path will be different on your machine. This makes the generated code not portable. But for this project, we don't have a need for portability.
-
- Compile the Java program
-
./gradlew installDist
-
- Run the Java program
- Make sure you are using Java 21.
-
build/install/jextract/bin/jextract
- It should look something like this:
$ build/install/jextract/bin/jextract Let's call native code from Java! Here we go... Your lucky number is 123.
General clean-ups, TODOs and things I wish to implement for this project:
- Engage foreign memory. Do something with C strings, structs, and/or pointers.
- Modularize the program (idiomatic). I was having some Gradle issue when I tried. I think it's a Gradle toolchain defect.
- Consider baking the dylib into the program distribution. Not a big deal.
- GitHub repo:
openjdk/jextract
- GitHub repo:
carldea/panama4newbies
- This is exactly what I'm trying to learn. Wow, thanks for the great content Carl! This is the accompaniment repo to the Foojay articles written by Carl.
- foojay.io: Project Panama for Newbies (Part 1)
- foojay.io: Building Project Panama’s jextract tool by yourself
- I might need to build
jextract
from source. Not sure yet.
- I might need to build
- foojay.io: Java Panama Polyglot (C++) Part 1
- Great example.