zerodds-endpoint-java 1.0 — Spec Coverage
Source: docs/specs/zerodds-endpoint-java-1.0.md — the ZeroDDS Java
endpoint SDK spec. Reliable-stream contract details in
docs/spec-coverage/reliable-endpoint-1.0.en.md. This document covers transport; marshalling is
produced by the shared code generator (crates/lang-rules, the
Language::Java column) rather than by a Java-specific generator. The former
codegen coverage zerodds-xcdr2-java went with crates/idl-java
(commit fa2ed8b0a9c).
Implementation:
endpoints/java/ZdwEndpoint.java(default package) — XRCE framing (xrceWriteFrame/xrceReadBody), serial framing after Annex C (serialFrame/serialDeframe/fcs16).endpoints/java/Zdw.java(default package) — wire core (Writer/Reader, XCDR2).endpoints/java/org/zerodds/endpoint/ReliableWire.java— reliable wire codec (HEARTBEAT/ACKNACK/WRITE_DATA) + RFC-1982 comparisons.endpoints/java/org/zerodds/endpoint/ReliableSender.java/ReliableReceiver.java— reliable sender/receiver state machine.endpoints/java/org/zerodds/endpoint/AsyncReliableWriter.java— async-decoupled reliable writer (BlockingQueue+ drainThread).crates/endpoint-e2e/tests/java.rs— ping-pong E2E;crates/endpoint-e2e/tests/java_reliable.rs— reliable-stream E2E + unit/golden + latency bench.endpoints/java/EndpointTest.java— byte-golden for WRITE_DATA/serial/DATA/heartbeat-read/ACKNACK (run manually againstcargo run -p zerodds-endpoint-golden, not wired intocargo test).
§1 XRCE framing
Spec: §1 — 8-byte XRCE header (session, stream, seq LE, submsg id 0x07
WRITE_DATA, flags, len LE) + body, byte-identical to crates/xrce +
endpoints/c; DATA receive path (id 0x09) through the same unwrap;
serial framing after Annex C.1 (addresses, length, FCS-16 of RFC 1662).
Repo: endpoints/java/ZdwEndpoint.java — xrceWriteFrame/xrceReadBody,
constants SESSION_NOKEY (0x80)/STREAM_BEST_EFFORT (0x01),
serialFrame/serialDeframe/fcs16, heartbeatRead,
acknackFrame.
Tests: crates/endpoint-e2e/tests/java.rs::java_endpoint_golden runs
endpoints/java/EndpointTest.java against real Rust goldens generated with
zerodds-endpoint-golden. This is the Java counterpart to
endpoints/c/Makefile’s make test GOLDEN_DIR=…: the test generates the five
golden files (golden_xrce_le.bin, golden_serial_le.bin,
golden_data_le.bin, golden_heartbeat_le.bin, golden_acknack_le.bin),
compiles EndpointTest together with Zdw and ZdwEndpoint, and asserts on
ALL OK and the exit status. Unlike ReliableSelfTest, EndpointTest carries
no hardcoded fallback goldens, so a failure to generate them fails the test
rather than quietly skipping the comparison.
Run on the Linux test host (tools/endpoints-gate, at 8bcb1d00161):
XRCE WRITE_DATA byte-identical (52 bytes)
serial byte-identical
DATA receive: body ok
serial deframe+crc round-trip ok
wrong frameCheck refused
0x7E inside the frame refused
HEARTBEAT parsed: first=1 last=3
ACKNACK byte-identical
appended submessage bounded out
over-long length rejected
truncated header rejected
writer >0xFFFF refused
writer maximal body framed
ALL OK
The framing is also exercised live via
java_endpoint_sync/java_endpoint_async (§4).
Status: done
§2 Sync send/recv
Spec: §2 — transport-opaque, no Client object: ZdwEndpoint is
stateless; the integrator owns the run loop and calls
xrceWriteFrame/xrceReadBody directly against its own transport (a socket
or an in-memory queue).
Repo: endpoints/java/ExampleSync.java — in-memory ArrayDeque poll
loop, full field decode (Reading{id,value,label}); sync mode in
crates/endpoint-e2e/tests/java.rs::MAIN_JAVA_TAIL — blocking
DatagramSocket.receive() against the real Rust peer.
Tests: java_endpoint_sync (§4).
Status: done.
§3 Async Reader/Writer
Spec: §3 — a reader Thread drains the transport into a
BlockingQueue; the consumer blocks on take(). No dedicated
AsyncReader/AsyncWriter class for the plain path (unlike
endpoints/go) — the integrator composes it from the stateless
ZdwEndpoint framing methods.
Repo: endpoints/java/ExampleAsync.java — reader Thread +
LinkedBlockingQueue<byte[]> inbox, full field decode; async mode in
crates/endpoint-e2e/tests/java.rs::MAIN_JAVA_TAIL — a reader Thread reads
from the real DatagramSocket, the consumer blocks on take().
Tests: java_endpoint_async (§4).
Status: done.
§4 Ping-pong E2E (live)
Spec: §5.1 — a Java app exchanges a typed sample with the shared Rust
XRCE peer over a real UDP socket: the generated Ping/Pong codec + the
endpoint SDK (ZdwEndpoint), sync and async.
Repo: crates/endpoint-e2e/tests/java.rs — MAIN_JAVA_HEAD (imports and
class header) and MAIN_JAVA_TAIL (the run loop, mode sync/async via CLI
argument) wrap the members of the generated ZdCodec; split_generated lifts
them out of that class, because the generator emits them private static and
nothing outside the class could otherwise call them. Compiled against
runtime/java (org.zerodds.wire.*) — the former runtime
crates/java-omgdds + crates/idl-java/runtime went with the IDL transplant.
Tests: (this environment, OpenJDK 21, cargo test -p zerodds-endpoint-e2e --test java)
- java_endpoint_sync — full stack via blocking DatagramSocket.receive().
- java_endpoint_async — full stack via reader Thread/LinkedBlockingQueue.
- java_endpoint_golden — EndpointTest.java against the Rust goldens (§1).
3 passed, 0 failed (verified locally).
Status: done.
§5 Reliable stream — state machine, wire, async writer
Spec: §4 (references reliable-endpoint v1.0 §3/§4) — XRCE reliable
stream (stream_id 0x80, §8.4.10/§8.4.11), mirroring the reference
crates/xrce/src/reliable.rs: ReliableSender.submit/pendingHeartbeat/
recvAcknack/getInFlight; ReliableReceiver.recvData/drainInOrder/
pendingAcknack/reset. Window 16, receiver buffer 64, heartbeat 500 ms,
payload ≤ 65535, RFC-1982 16-bit sequence numbers. Alongside it, the
async-decoupled AsyncReliableWriter: the producer enqueues wait-free onto
a buffered BlockingQueue (submit/offer), a dedicated drain Thread
holds the ReliableSender state and does all the I/O (send, heartbeat,
ACKNACK-driven retransmit) — the producer never enters the kernel.
Repo: endpoints/java/org/zerodds/endpoint/ReliableWire.java —
writeFrame, heartbeatFrame/parseHeartbeat, acknackFrame/parseAckNack,
seqLt/seqGt; ReliableSender, ReliableReceiver; AsyncReliableWriter
(ArrayBlockingQueue<byte[]> capacity 4096, drain Thread
zdw-reliable-drain, submit/offer/finish/delivered); example app
endpoints/java/ExampleReliable.java (a real UDP peer, no in-process stub).
Tests: (this environment, cargo test -p zerodds-endpoint-e2e --test java_reliable)
- java_reliable_unit_and_golden — compiles and runs
endpoints/java/ReliableSelfTest.java: 33 check() assertions across 17
test scenarios (sender: monotonic seq, in-flight count,
payload-too-large, window-full, heartbeat first/silence/after-500ms,
no-heartbeat-when-empty, acknack partial/full clear; receiver: in-order
drain, reorder, duplicate dropped, buffer-full, pending-acknack bitmap,
reset; in-process end-to-end loss recovery; byte-golden for
HEARTBEAT/ACKNACK — hardcoded and, when
cargo run -p zerodds-endpoint-golden is available, additionally checked
against the generated golden_heartbeat_le.bin/golden_acknack_le.bin).
Byte-golden: HeartbeatFrame(1,3) ==
80 00 01 00 0b 01 05 00 01 00 03 00 80, AckNackFrame(1,0) ==
80 00 01 00 0a 01 05 00 01 00 00 00 80 — identical to the reference
goldens (same bytes as endpoints/go). Output: ALL OK.
- java_reliable_loss_recovery — the peer drops every 3rd sample once; the
app (ExampleReliable, AsyncReliableWriter) retransmits on ACKNACK; all
12 samples delivered gap-free in order.
- java_reliable_no_loss_baseline — lossless baseline; 12/12.
3/3 passed (verified locally; latency bench in §6).
Status: done.
§6 Latency — BlockingQueue enqueue vs. inline DatagramSocket.send
Spec: §5.3 — the producer path of AsyncReliableWriter (offer →
BlockingQueue push) must be measurably below the inline
DatagramSocket.send syscall — the evidence that async write removes
syscall latency from the producer path, not that it waits for ACKNACK.
Repo: endpoints/java/ReliableBench.java — 20000 iterations of inline
DatagramSocket.send (a real, bound loopback destination socket, never
read) vs. 20000 iterations of BlockingQueue.offer (a drain Thread keeps
the queue empty, the way AsyncReliableWriter’s real drain thread would),
no live peer needed.
Tests: (this environment, java_reliable_latency_bench)
producer latency: queue-enqueue median = 84 ns, inline-send median = 5917 ns (70x)
Median over 20000 iterations per path, measured locally (macOS, not a
Linux run) — the order of magnitude matches endpoints/go’s
Linux-verified 20–25 ns / 4360 ns (~175–220×); the absolute value is
machine-/load-dependent, the ratio (enqueue well below the syscall) is the
evidence.
Status: done.
Audit status
6 done / 0 partial / 0 open / 0 blocked / 0 n/a (informative).
Test run (this environment — macOS, OpenJDK 21, javac/java on PATH —
not Linux-verified): cargo test -p zerodds-endpoint-e2e --test java
→ 3 passed, 0 failed (java_endpoint_sync, java_endpoint_async,
java_endpoint_golden); --test java_reliable → 4 passed, 0 failed
(java_reliable_loss_recovery, java_reliable_no_loss_baseline,
java_reliable_unit_and_golden — 33 Java unit assertions across 17 scenarios
including byte goldens, java_reliable_latency_bench — queue enqueue 83 ns
against inline send 6542 ns, ~79x).
Open items: none.
zerodds-endpoint-java 1.0 — Spec-Coverage
Quelle: docs/specs/zerodds-endpoint-java-1.0.md — ZeroDDS Java Endpoint-SDK-Spec.
Reliable-Stream-Kontraktdetails in docs/spec-coverage/reliable-endpoint-1.0.md.
Hier der Transport; das Marshalling erzeugt der gemeinsame Codegen
(crates/lang-rules, Spalte Language::Java) und nicht mehr ein
Java-eigener Generator. Die frühere Codegen-Coverage zerodds-xcdr2-java
ist mit crates/idl-java entfallen (Commit fa2ed8b0a9c).
Implementation:
endpoints/java/ZdwEndpoint.java(Default-Package) — XRCE-Framing (xrceWriteFrame/xrceReadBody), serielles Framing nach Annex C (serialFrame/serialDeframe/fcs16).endpoints/java/Zdw.java(Default-Package) — Wire-Core (Writer/Reader, XCDR2).endpoints/java/org/zerodds/endpoint/ReliableWire.java— reliable Wire-Codec (HEARTBEAT/ACKNACK/WRITE_DATA) + RFC-1982-Vergleiche.endpoints/java/org/zerodds/endpoint/ReliableSender.java/ReliableReceiver.java— reliable Sender-/Receiver-State-Machine.endpoints/java/org/zerodds/endpoint/AsyncReliableWriter.java— async-entkoppelter reliable Writer (BlockingQueue+ Drain-Thread).crates/endpoint-e2e/tests/java.rs— Ping-Pong-E2E;crates/endpoint-e2e/tests/java_reliable.rs— reliable-Stream-E2E + Unit/Golden + Latenz-Bench.endpoints/java/EndpointTest.java— Byte-Golden für WRITE_DATA/serial/DATA/HEARTBEAT-Read/ACKNACK (manuell gegencargo run -p zerodds-endpoint-goldengefahren, nicht übercargo testverdrahtet).
§1 XRCE-Framing
Spec: §1 — 8-Byte-XRCE-Header (session, stream, seq LE, submsg id 0x07
WRITE_DATA, flags, len LE) + Body, byte-identisch zu crates/xrce +
endpoints/c; DATA-Empfangspfad (id 0x09) über denselben Unwrap; serielles
Framing nach Annex C.1 (Adressen, Länge, FCS-16 nach RFC 1662).
Repo: endpoints/java/ZdwEndpoint.java — xrceWriteFrame/xrceReadBody,
Konstanten SESSION_NOKEY (0x80)/STREAM_BEST_EFFORT (0x01),
serialFrame/serialDeframe/fcs16, heartbeatRead,
acknackFrame.
Tests: crates/endpoint-e2e/tests/java.rs::java_endpoint_golden fährt
endpoints/java/EndpointTest.java gegen echte, mit zerodds-endpoint-golden
erzeugte Rust-Goldens. Das ist das Java-Gegenstück zu endpoints/c/Makefiles
make test GOLDEN_DIR=…: der Test erzeugt die fünf Golden-Dateien
(golden_xrce_le.bin, golden_serial_le.bin, golden_data_le.bin,
golden_heartbeat_le.bin, golden_acknack_le.bin), übersetzt EndpointTest
mit Zdw und ZdwEndpoint und prüft ALL OK sowie den Exit-Code. Anders als
ReliableSelfTest trägt EndpointTest keine fest verdrahteten Ersatz-Goldens,
deshalb lässt der Test ein Fehlschlagen der Golden-Erzeugung fehlschlagen statt
den Vergleich still zu überspringen.
Lauf auf dem Linux-Testhost (tools/endpoints-gate, Stand 8bcb1d00161):
XRCE WRITE_DATA byte-identical (52 bytes)
serial byte-identical
DATA receive: body ok
serial deframe+crc round-trip ok
wrong frameCheck refused
0x7E inside the frame refused
HEARTBEAT parsed: first=1 last=3
ACKNACK byte-identical
appended submessage bounded out
over-long length rejected
truncated header rejected
writer >0xFFFF refused
writer maximal body framed
ALL OK
Das Framing wird zusätzlich live über java_endpoint_sync/java_endpoint_async
(§4) geübt.
Status: done
§2 sync send/recv
Spec: §2 — transport-opak, kein Client-Objekt: ZdwEndpoint ist
zustandslos; der Integrator besitzt den Run-Loop und ruft
xrceWriteFrame/xrceReadBody direkt gegen seinen eigenen Transport (Socket
oder In-Memory-Queue) auf.
Repo: endpoints/java/ExampleSync.java — In-Memory-ArrayDeque-Poll-Loop,
volles Feld-Decode (Reading{id,value,label}); sync-Modus in
crates/endpoint-e2e/tests/java.rs::MAIN_JAVA_TAIL — blockierendes
DatagramSocket.receive() gegen den echten Rust-Peer.
Tests: java_endpoint_sync (§4).
Status: done.
§3 async Reader/Writer
Spec: §3 — Reader-Thread drainiert den Transport in eine
BlockingQueue, Consumer blockiert auf take(); keine dedizierte
AsyncReader/AsyncWriter-Klasse für den Plain-Pfad (anders als
endpoints/go) — der Integrator komponiert sie aus den zustandslosen
ZdwEndpoint-Framing-Methoden.
Repo: endpoints/java/ExampleAsync.java — Reader-Thread +
LinkedBlockingQueue<byte[]>-Inbox, volles Feld-Decode; async-Modus in
crates/endpoint-e2e/tests/java.rs::MAIN_JAVA_TAIL — Reader-Thread liest vom
echten DatagramSocket, Consumer blockiert auf take().
Tests: java_endpoint_async (§4).
Status: done.
§4 Ping-Pong-E2E (live)
Spec: §5.1 — eine Java-App tauscht mit dem geteilten Rust-XRCE-Peer über
einen echten UDP-Socket ein typisiertes Sample aus: generierter
Ping/Pong-Codec + Endpoint-SDK (ZdwEndpoint), sync und async.
Repo: crates/endpoint-e2e/tests/java.rs — MAIN_JAVA_HEAD (Imports und
Klassenkopf) und MAIN_JAVA_TAIL (Run-Loop, Modus sync/async per
CLI-Argument) umschließen die Member des generierten ZdCodec; split_generated
hebt sie aus der Klasse, weil der Generator sie private static schreibt und
sie sonst von außen nicht erreichbar wären. Kompiliert gegen runtime/java
(org.zerodds.wire.*) — die frühere Runtime crates/java-omgdds +
crates/idl-java/runtime ist mit dem IDL-Transplantat entfallen.
Tests: (dieses Environment, OpenJDK 21, cargo test -p zerodds-endpoint-e2e --test java)
- java_endpoint_sync — voller Stack über blockierendes DatagramSocket.receive().
- java_endpoint_async — voller Stack über Reader-Thread/LinkedBlockingQueue.
- java_endpoint_golden — EndpointTest.java gegen die Rust-Goldens (§1).
3 passed, 0 failed (lokal verifiziert).
Status: done.
§5 Reliable Stream — State-Machine, Wire, Async-Writer
Spec: §4 (verweist auf reliable-endpoint v1.0 §3/§4) — XRCE reliable
Stream (stream_id 0x80, §8.4.10/§8.4.11), spiegelt die Referenz
crates/xrce/src/reliable.rs: ReliableSender.submit/pendingHeartbeat/
recvAcknack/getInFlight; ReliableReceiver.recvData/drainInOrder/
pendingAcknack/reset. Fenster 16, Receiver-Buffer 64, Heartbeat 500 ms,
Payload ≤ 65535, RFC-1982 16-bit Sequenznummern. Dazu der
async-entkoppelte AsyncReliableWriter: der Producer enqueued wait-free in
eine gepufferte BlockingQueue (submit/offer), ein dedizierter
Drain-Thread hält den ReliableSender-State und macht die gesamte I/O
(senden, Heartbeat, ACKNACK-getriebenes Retransmit) — der Producer geht nie
in den Kernel.
Repo: endpoints/java/org/zerodds/endpoint/ReliableWire.java —
writeFrame, heartbeatFrame/parseHeartbeat, acknackFrame/parseAckNack,
seqLt/seqGt; ReliableSender, ReliableReceiver; AsyncReliableWriter
(ArrayBlockingQueue<byte[]> Kapazität 4096, Drain-Thread
zdw-reliable-drain, submit/offer/finish/delivered); Beispiel-App
endpoints/java/ExampleReliable.java (echter UDP-Peer, kein In-Process-Stub).
Tests: (dieses Environment, cargo test -p zerodds-endpoint-e2e --test java_reliable)
- java_reliable_unit_and_golden — kompiliert und läuft
endpoints/java/ReliableSelfTest.java: 33 check()-Assertionen über 17
Testszenarien (Sender: monotone seq, in-flight count, payload-too-large,
window-full, heartbeat first/silence/after-500ms, no-heartbeat-when-empty,
acknack partial/full clear; Receiver: in-order drain, reorder, duplicate
dropped, buffer-full, pending-acknack-Bitmap, reset; End-to-End
Loss-Recovery in-process; Byte-Golden für HEARTBEAT/ACKNACK — hardcoded
und, wenn cargo run -p zerodds-endpoint-golden verfügbar ist,
zusätzlich gegen die generierten golden_heartbeat_le.bin/
golden_acknack_le.bin geprüft). Byte-Golden: HeartbeatFrame(1,3) ==
80 00 01 00 0b 01 05 00 01 00 03 00 80, AckNackFrame(1,0) ==
80 00 01 00 0a 01 05 00 01 00 00 00 80 — identisch zu den
Referenz-Goldens (gleiche Bytes wie endpoints/go). Ausgabe: ALL OK.
- java_reliable_loss_recovery — Peer dropt jedes 3. Sample einmalig; die
App (ExampleReliable, AsyncReliableWriter) retransmittet auf ACKNACK;
alle 12 Samples lückenlos in Reihenfolge geliefert.
- java_reliable_no_loss_baseline — lossless Baseline; 12/12.
3/3 grün (lokal verifiziert; Latenz-Bench in §6).
Status: done.
§6 Latenz — BlockingQueue-Enqueue vs. inline DatagramSocket.send
Spec: §5.3 — der Producer-Pfad des AsyncReliableWriter
(offer → BlockingQueue-Push) muss messbar unter dem inline
DatagramSocket.send-Syscall liegen — der Beleg, dass Async-Write die
Syscall-Latenz aus dem Producer-Pfad nimmt, nicht das Warten auf ACKNACK.
Repo: endpoints/java/ReliableBench.java — 20000 Iterationen inline
DatagramSocket.send (echter, gebundener Loopback-Zielsocket, nie gelesen)
vs. 20000 Iterationen BlockingQueue.offer (Drain-Thread hält die Queue
leer, wie AsyncReliableWriters echter Drain-Thread), kein Live-Peer
nötig.
Tests: (dieses Environment, java_reliable_latency_bench)
producer latency: queue-enqueue median = 84 ns, inline-send median = 5917 ns (70x)
Median über 20000 Iterationen je Pfad, lokal gemessen (macOS, kein
Lauf auf dem Linux-Testhost) — Größenordnung deckt sich mit endpoints/gos
Linux-verifizierten 20–25 ns / 4360 ns (~175–220×); der Absolutwert ist
maschinen-/lastabhängig, das Verhältnis (Enqueue deutlich unter Syscall)
ist der Beleg.
Status: done.
Audit-Status
6 done / 0 partial / 0 open / 0 blocked / 0 n/a (informative).
Test-Lauf: dieses Environment — macOS, OpenJDK 21, javac/java auf PATH —
nicht Linux-verifiziert — cargo test -p zerodds-endpoint-e2e --test java
→ 3 passed, 0 failed (java_endpoint_sync, java_endpoint_async,
java_endpoint_golden); --test java_reliable → 4 passed, 0 failed
(java_reliable_loss_recovery, java_reliable_no_loss_baseline,
java_reliable_unit_and_golden — 33 Java-Unit-Assertionen über 17 Szenarien
inkl. Byte-Golden, java_reliable_latency_bench — Queue-Enqueue 83 ns gegen
inline send 6542 ns, ~79×).
Offene Punkte: keine.