Overview
Create the NativeBinding interface, JnaNativeBinding JNA-backed implementation, and OutboundCallback JNA callback for the 5 C ABI entry points of the Copilot runtime.
This is task 4.4 of 9 in the implementation plan. Tasks are assigned, completed, and merged serially in this listed order. Tasks 4.1–4.3 are complete on the base branch before this task begins.
Branch: edburns/1917-java-embed-rust-cli-runtime-dd-3039145 on upstream
Plan and supporting resources
On the edburns/1917-java-embed-rust-cli-runtime-dd-3039145 branch, the directory 1917-java-embed-rust-cli-runtime-remove-before-merge contains the plan (1917-embed-cli-runtime-ignorance-reduction-plan.md) and supporting resources (spikes, prototypes, diagrams).
Read the entire plan before working.
Relevant plan sections to carefully re-read
- Section 3.3 — JNA binding interface design — Resolution:
NativeBinding is a Java interface with default methods, not an abstract class. Direct instantiation (no ServiceLoader). Package com.github.copilot.ffi.
- Section 3.4 — JNA callback threading and lifecycle — Resolution: Use
QueueInputStream (not PipedInputStream — rejected). Hold Callback as strong-reference field. AtomicInteger for active callback tracking. Read the spike at 1917-java-embed-rust-cli-runtime-remove-before-merge/spike-3-4-jna-callback-and-threading/, specifically the java-program-that-invokes-rust-dll-mr-jar-17-25/ directory (✅ the approved approach).
- Section 3.8 — JNA dependency management — Resolution: JNA core 5.19.1,
<optional>true</optional>. Pin version, do not use range. No jna-platform. No GraalVM native-image support.
- Section 3.9 — C ABI parameter semantics — Resolution: Complete parameter specification for all 5 entry points. Wire format is LSP
Content-Length header framing. Buffer copied synchronously by native side.
- C ABI entry points to bind (table at top of plan) — the 5
extern "C" functions: copilot_runtime_host_start, copilot_runtime_host_shutdown, copilot_runtime_connection_open, copilot_runtime_connection_write, copilot_runtime_connection_close.
- Section 4.4 — JNA binding interface and implementation (the primary task description).
- TDD discipline for all implementation steps — write tests first using the test native library from
spike-3-4-jna-callback-and-threading/rust-dll/.
Resolved decisions that constrain this task
- JNA version: 5.19.1,
<optional>true</optional> in the SDK POM. Only net.java.dev.jna:jna (no jna-platform).
- Interface, not abstract class:
NativeBinding is a Java interface for future FFM swappability.
- Direct instantiation: The transport class instantiates
JnaNativeBinding directly. No ServiceLoader.
- Library-never-unloads pattern: The loaded native handle must be held in a
static field and never released. JNA caches by library name, but this must be explicit since native worker threads outlive any FfiRuntimeHost instance. See Rust OnceLock<Mutex<HashMap<PathBuf, &'static Library>>> + Box::leak(). Missing this risks a crash if a second FfiRuntimeHost is created after the first is closed.
- Duplicate library guard: If a library is already loaded from path A and a request comes for path B, throw
IllegalStateException with diagnostic message (from Section 3.10 Resolution).
- Callback GC protection: Hold JNA
Callback as a strong-reference field. If GC'd → dangling function pointer → JVM crash.
- Active callback tracking:
AtomicInteger count of active callbacks, mirroring Rust's AtomicUsize.
- Callback buffer copying: Use
Pointer.getByteArray(0, len) — pointer only valid during callback invocation.
- No GraalVM native-image support for InProcess transport (Section 3.8 Resolution, DRI decision).
Deliverables
Files to create
-
java/sdk/src/main/java/com/github/copilot/ffi/NativeBinding.java — Interface with methods:
int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen)
boolean hostShutdown(int serverId)
int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen)
boolean connectionWrite(int connectionId, byte[] data, int dataLen)
boolean connectionClose(int connectionId)
-
java/sdk/src/main/java/com/github/copilot/ffi/JnaNativeBinding.java — JNA implementation:
- Defines a JNA
Library interface mapping the 5 extern "C" exports.
- Loads the native library by absolute path (from
NativeRuntimeLoader).
- Static field holding the loaded library (never unloaded).
- Process-wide guard preventing loading a different library path.
- Delegates method calls to the JNA library interface.
-
java/sdk/src/main/java/com/github/copilot/ffi/OutboundCallback.java — JNA Callback interface:
void invoke(Pointer userData, Pointer data, int len) (or NativeLong for size_t)
- Invoked by native code on native threads.
-
java/sdk/src/test/java/com/github/copilot/ffi/JnaNativeBindingTest.java — Unit tests:
- Load a test native library (from spike-3-4
rust-dll/), call functions, receive callbacks.
- Error cases throw
IllegalStateException.
- Duplicate library load from different path throws
IllegalStateException with diagnostic message.
- Callback invocation increments/decrements active callback count.
- At least one success-path test and one failure/edge-case test per public method.
JNA dependency addition
Add to java/sdk/pom.xml:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.19.1</version>
<optional>true</optional>
</dependency>
Keep the version in a Maven property for deliberate upgrades.
Gating tests and criteria
- Unit tests pass: All tests in
JnaNativeBindingTest pass using the spike-3-4 test native library.
- Library load and call: Can load a native library by path, call exported functions, receive callbacks on native threads.
- Duplicate load guard: Loading from path A then path B throws
IllegalStateException.
- Callback tracking:
AtomicInteger correctly tracks active callback count.
- All prior tests pass:
mvn verify from java/ passes.
- Spotless compliance:
mvn spotless:check passes.
Out of scope
FfiRuntimeHost lifecycle management (task 4.5).
QueueInputStream, FfiOutputStream, or stream bridging (task 4.5).
CopilotClient integration (task 4.6).
- Testing with real
runtime.node binary (task 4.8).
Overview
Create the
NativeBindinginterface,JnaNativeBindingJNA-backed implementation, andOutboundCallbackJNA callback for the 5 C ABI entry points of the Copilot runtime.This is task 4.4 of 9 in the implementation plan. Tasks are assigned, completed, and merged serially in this listed order. Tasks 4.1–4.3 are complete on the base branch before this task begins.
Branch:
edburns/1917-java-embed-rust-cli-runtime-dd-3039145onupstreamPlan and supporting resources
On the
edburns/1917-java-embed-rust-cli-runtime-dd-3039145branch, the directory1917-java-embed-rust-cli-runtime-remove-before-mergecontains the plan (1917-embed-cli-runtime-ignorance-reduction-plan.md) and supporting resources (spikes, prototypes, diagrams).Read the entire plan before working.
Relevant plan sections to carefully re-read
NativeBindingis a Javainterfacewith default methods, not an abstract class. Direct instantiation (noServiceLoader). Packagecom.github.copilot.ffi.QueueInputStream(notPipedInputStream— rejected). HoldCallbackas strong-reference field.AtomicIntegerfor active callback tracking. Read the spike at1917-java-embed-rust-cli-runtime-remove-before-merge/spike-3-4-jna-callback-and-threading/, specifically thejava-program-that-invokes-rust-dll-mr-jar-17-25/directory (✅ the approved approach).<optional>true</optional>. Pin version, do not use range. Nojna-platform. No GraalVM native-image support.Content-Lengthheader framing. Buffer copied synchronously by native side.extern "C"functions:copilot_runtime_host_start,copilot_runtime_host_shutdown,copilot_runtime_connection_open,copilot_runtime_connection_write,copilot_runtime_connection_close.spike-3-4-jna-callback-and-threading/rust-dll/.Resolved decisions that constrain this task
<optional>true</optional>in the SDK POM. Onlynet.java.dev.jna:jna(nojna-platform).NativeBindingis a Javainterfacefor future FFM swappability.JnaNativeBindingdirectly. NoServiceLoader.staticfield and never released. JNA caches by library name, but this must be explicit since native worker threads outlive anyFfiRuntimeHostinstance. See RustOnceLock<Mutex<HashMap<PathBuf, &'static Library>>>+Box::leak(). Missing this risks a crash if a secondFfiRuntimeHostis created after the first is closed.IllegalStateExceptionwith diagnostic message (from Section 3.10 Resolution).Callbackas a strong-reference field. If GC'd → dangling function pointer → JVM crash.AtomicIntegercount of active callbacks, mirroring Rust'sAtomicUsize.Pointer.getByteArray(0, len)— pointer only valid during callback invocation.Deliverables
Files to create
java/sdk/src/main/java/com/github/copilot/ffi/NativeBinding.java— Interface with methods:int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen)boolean hostShutdown(int serverId)int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen)boolean connectionWrite(int connectionId, byte[] data, int dataLen)boolean connectionClose(int connectionId)java/sdk/src/main/java/com/github/copilot/ffi/JnaNativeBinding.java— JNA implementation:Libraryinterface mapping the 5extern "C"exports.NativeRuntimeLoader).java/sdk/src/main/java/com/github/copilot/ffi/OutboundCallback.java— JNACallbackinterface:void invoke(Pointer userData, Pointer data, int len)(orNativeLongforsize_t)java/sdk/src/test/java/com/github/copilot/ffi/JnaNativeBindingTest.java— Unit tests:rust-dll/), call functions, receive callbacks.IllegalStateException.IllegalStateExceptionwith diagnostic message.JNA dependency addition
Add to
java/sdk/pom.xml:Keep the version in a Maven property for deliberate upgrades.
Gating tests and criteria
JnaNativeBindingTestpass using the spike-3-4 test native library.IllegalStateException.AtomicIntegercorrectly tracks active callback count.mvn verifyfromjava/passes.mvn spotless:checkpasses.Out of scope
FfiRuntimeHostlifecycle management (task 4.5).QueueInputStream,FfiOutputStream, or stream bridging (task 4.5).CopilotClientintegration (task 4.6).runtime.nodebinary (task 4.8).