# jextract: calling SQLite from Java with the FFM API Companion code for the ankurm.com post **"Calling C from Java with jextract and the FFM API: A Real Library, No JNI"**. No `docs/` folder: the explanation lives in the article. | Thing | Version | |---|---| | JDK | Temurin 25.0.5+8 (FFM API final since JDK 22, JEP 454) | | jextract | `openjdk-25-jextract+2-4` early-access build, 2025-11-25 (jdk.java.net/jextract), libclang 13.0.0 | | SQLite | 3.45.1 (Ubuntu `libsqlite3-dev 3.45.1-1ubuntu2.8`) | | JMH | 1.37 | | gcc | 13.3.0 (only for the JNI comparison) | ## Quickstart ```bash sudo apt-get install libsqlite3-dev gcc # header + libsqlite3.so; gcc only for the JNI benchmark export JAVA_HOME=/path/to/jdk-25 ./scripts/fetch-jextract.sh # downloads and sha256-checks the jextract build into ./tools export JEXTRACT=$PWD/tools/jextract-25/bin/jextract ./scripts/gen.sh # regenerate generated/ (the committed bindings) ./scripts/run-all.sh # compile everything, regenerate output/01..09 and 11 ./scripts/fetch-jmh.sh && ./scripts/run-jmh.sh # the FFM vs JNI vs plain Java benchmark, about 4 minutes -> output/10-jmh.txt ``` Run any demo by hand: `java --enable-native-access=ALL-UNNAMED -cp build/classes demo.Tour`. ## Layout | Path | What | |---|---| | `scripts/gen.sh` | the jextract command, filtered to 25 functions and 13 constants | | `generated/demo/sqlite/` | what jextract produced from that command (4 files, committed so you can read them) | | `src/demo/` | hand-written code: `Db` (the wrapper), `Hello`, `Tour`, `Arenas`, `Errors`, `Trap`, `NullText`, `Callbacks` | | `src/extra/V.java` | one-line program used by `run-all.sh` to test library loading with throwaway bindings | | `jni/` | the JNI twin used by the benchmark: `SqliteJni.java` and `sqlite_jni.c` | | `bench/src/bench/SqliteBench.java` | JMH benchmark: plain Java vs FFM vs JNI | ## Captured output (`output/`) | File | What it is | |---|---| | `01-tools.txt` | versions of every tool used | | `02-generate.txt` | the trimmed generation vs the whole header: file counts, line counts | | `03-hello.txt` | first call, plus `-Djextract.trace.downcalls=true` | | `04-native-access.txt` | JDK 25 with no `--enable-native-access` flag: the warning, and `--illegal-native-access=deny` | | `05-tour.txt` | open, create, bind, insert, query | | `06-arenas.txt` | confined, shared, auto and global arenas used wrongly | | `07-pointer-traps.txt` | zero-length pointers, and the `SIGSEGV` from `getString` on a NULL pointer | | `08-errors.txt` | return codes turned into exceptions | | `09-callbacks.txt` | `sqlite3_exec` with an upcall, and an exception thrown inside it | | `10-jmh.txt` | JMH numbers (machine dependent: shape, not result) | | `11-library-loading.txt` | what `-l` produces, and the failure when the library name is wrong | Timings and addresses change between runs; everything else is byte-stable. The Linux/x64 jextract build is used throughout; nothing here was run on macOS or Windows.