Skip to content

feat(bindings): a Java binding over the C API - #575

Closed
aditipolkam wants to merge 7 commits into
mainfrom
feat/java-bindings
Closed

aditipolkam wants to merge 7 commits into
mainfrom
feat/java-bindings

Conversation

@aditipolkam

Copy link
Copy Markdown
Member

Summary

Adds bindings/java: a JNA wrapper over the C API for the JVM on desktop and server (Java 11+, 64-bit). It is the local API called in process, so it parses no JSON and a method added to the interface definition needs no change here.

  • OfflineProtocol (AutoCloseable): create, call / callStatic by local API method name, event and transport listeners, pollEvent, initializeMls with a Java StorageProvider, and runStorageConformance.
  • OfflineProtocolException carries the status and the definition's ProtocolError variant and position.
  • Callbacks are static JNA singletons keyed by an id in ctx; a registration is released only by the library's drop(ctx).
  • close() waits for calls in flight, matching the C contract.
  • The native library is not bundled: set -Djna.library.path or -Doffline_protocol.library.

Contract: docs/bridges/java.md. Guide: bindings/java/README.md.

Tests

  • ConsumerCheckTest (12 tests) loads the real library, the counterpart of bindings/c/consumer-check/main.c.
  • HeaderDriftTest (2 tests) fails if the JNA declarations or the Status, Transport and storage Kind values drift from the committed C header.
  • New java-binding CI job on Linux and macOS (compiles with -Xlint:all -Werror, runs the tests, builds the Javadoc and sources jars).

Run locally on macOS, JDK 17, Gradle 9.3.1: gradle build passes, 14 tests. The CI job has not run yet, and CI pins Gradle 8.9, which I did not try.

Not in this PR

  • Release packaging or Maven Central publishing.
  • Bundling the native library in the jar.

🤖 Generated with Claude Code

The binding is a JVM wrapper over the C API through JNA, so the build has
no native step: it loads the library the desktop releases already ship.
Java 11 or later, javac warnings are errors, and the tests take the
library directory from -PnativeLibraryDir (default: this checkout's Cargo
debug output).
… types

NativeApi declares the functions, structures and callback signatures of
offline_protocol.h. Strings the library hands out are read as UTF-8 and
released only with offline_protocol_string_free, never copied by JNA and
left to leak, and every string in is UTF-8 whatever the JVM's default
charset is. size_t is a long, so a 32-bit JVM is refused at load time
rather than corrupting memory. OfflineProtocolException carries the
status, the ProtocolError variant and its position.
…d transport notifications

Methods are called by their local API name with a JSON object, so the
class does not change when the definition grows a method and takes no JSON
library of its own. Event and transport listeners reach the library
through static callback singletons and an id in ctx, which keeps every JNA
callback reachable and lets the library's single drop(ctx) be the only
place a registration is released, refused ones included. close() takes the
write side of a lock the calls share, which is the contract's no call in
flight when the engine is freed. A listener's exception is swallowed rather
than unwound into Rust.
…rmance suite

StorageProvider is the four operations of the C provider as a Java
interface. A buffer handed back to the library is native memory held until
the library returns it through release, and an exception that is not the
provider's own StorageException is reported as the operation's failure,
never as corruption, which would make the engine mint a new protocol-state
key and strand every record sealed under the old one.
OfflineProtocol.initializeMls attaches two providers and
runStorageConformance runs the suite against one.
The counterpart of bindings/c/consumer-check/main.c, run by Gradle against
the Cargo build: the version and an instance-less call, events on the
calling thread and in the poll queue, every refusal status and the
definition's variant and position, a closed engine, drop(ctx) releasing
each listener registration (replaced ones and freed engines), a listener
that throws, a Java storage provider passing the conformance suite and
giving the engine an address with every buffer returned through release,
and the built-in file stores.
…he binding on Linux and macOS

HeaderDriftTest reads the committed header: every function in it is
declared in NativeApi and nothing else is, and the Status, Transport and
storage Kind values are the header's numbers. A function added to the
header and not declared here is unreachable from Java and fails no build.
The java-binding job builds the library, then compiles with -Xlint:all
-Werror, runs the consumer check and builds the Javadoc and sources jars.
…in the docs

bindings/java/README.md is the guide: what to load and how the JVM finds
it, a quick start, how methods are named and encoded, events, storage and
the build. docs/bridges/java.md is the contract: it is the C binding with
Java types, which shared rules reach it and how, why the declarations are
held to the header by a test, why a registration is released only by the
library's drop, and why a listener cannot close the engine. The binding
gets its row wherever the others have one: the docs index, the bridge
contract's table, the README's install table, CLAUDE.md's knowledge table
and commands, and the CHANGELOG.
@github-actions

Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@github-actions github-actions Bot locked and limited conversation to collaborators Oct 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant