Repository navigation
zenoh-flat transition (integration branch) - #482
Conversation
Tracking document for the rebuild of zenoh-java on the generated zenoh-flat-jni bindings. Constituent PRs target this branch; it merges to main as a whole when the transition completes (removing this file).
* initial code generation
* templates in separate files in prebindgen-ext
* mangle harness
* dependency on zenoh-flat-jni added
* switch to keyexprValidate, package support
* zenoh-jni reexported by zenoh-flat-jni
* No load generator
* output only declared in prebindgen-ext
* better error reporting
* reporting fix
* report unresolved descendants from required roots
BFS from each required-but-unresolved type through struct fields, enum
variants, generic args, and impl Fn args, surfacing transitively
unresolved entries that propagate_required cannot reach (because subs
edges don't exist past an unresolved parent). Without this, a missing
declaration in build.rs for a type only referenced as a field of another
unresolved struct went unreported.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactored, test added
* ptr class added
* qualify non-fqn types
* filter undeclared fns out of JNINative/JNIOrphaned emit
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* drop JNIOrphaned bucket; reuse declared_functions trait method
.method() is a namespace declaration — every #[prebindgen] fn must
be assigned to a typed handle, data class, enum, or kotlin_package.
There is no third "orphan" bucket, so JNIOrphaned was always emitted
empty and the duplicate declared_function_names helper just repeated
the PrebindgenExt::declared_functions trait method.
- delete write_jni_orphaned, jni_orphaned_class_name, the stale
generated JNIOrphaned.kt file
- rename render_jni_orphaned_source -> render_jni_package_source,
drop the now-redundant include_only_promoted / wrap_in_object /
declared / class_name params
- drop declared_function_names; render_jni_native_source now takes
&HashSet<syn::Ident> from declared_functions() directly
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* missing functions added to autogeneration
* into in build.rs
* universal post-emit qualification via VisitMut visitor
Replace the five scattered self.qualify_emitted_type() calls (build_input_fn,
build_output_fn, opaque_handle_input, build_handle_destructor_items,
option_input) with a single AST visitor pass applied once at write time.
The previous design qualified only at specific emit sites — each new emit
site had to remember the call. emit_into_dispatcher missed it, so the
impl-Into dispatcher body emitted bare KeyExpr/ZKeyExpr references that
failed to resolve in the binding crate's scope.
Architecture:
- New PrebindgenExt::post_process_item(item: &mut syn::Item) trait hook
(default no-op), invoked by write_rust on every emitted item.
- JniExt implements it via qualify_item, which runs QualifyEmittedTypes —
a syn::visit_mut::VisitMut that walks the full AST (signatures, generic
args, type ascriptions in let bindings, *mut/*const in cast expressions)
and prefixes every bare single-segment Type::Path matching a registered
source-module name with source_module.
- option_input no longer takes &JniExt; the body uses the bare t1 token
and the post-emit visitor qualifies it later.
Also fixes the pre-existing TypeEntry field-name drift in
final_invariant_reports_unresolved_field_of_unresolved_struct.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* transition to zenoh-flat keyexpr for zenoh-java
* ZKeyExpr improved
* closeable data classes
* universa close for data classes
* throwable data class
* config in zenoh-flat
* scouting api
* JNIScout removed
* whatami from jni
* value type
* zenoh id is a wrapper
* &str as input parameter
* logging api
* structured methods
* buildrs api renamr
* scouting package
* packaging fixes
* set intersection level
* zenoh-flat as mods
* qos enums ported
* enum validation in prebindgen
* wrap enums
* bytearray in zenoh-flat
* zenoh-java zbytes
* jni synchronized
* enum return fix
* take and ptr wraper in callback
* manually implemented serialization
* publisher ported
* attachment into
* encoding in zenoh-flat
* encoding constants
* renaming, fromstring added
* zencoding
* subscriber ported
* querier/queriable ported
* query and reply methods ported
* query structure
* renaming
* get and corresponding structures
* full timestamp representation in zenoh-flat
* old log init removed
* liveliness migrated, codegenerator fixed
* value type return fix
* sesstion migrated
* valur type handling fix
* niche fix
* wrappers directly in Session and. Liveliness
* bytes form zenoh_id to string added
* zenoh-jni removed
* jni generator and zenoh-flat moved out
* wrapper lock function
* blob type
* vlaue blob zzenohid
* missing api surface added
* switch to wrapped functions
* single into
* gtom struct from callback
* recursive call
* flatten callbacks
* create java object directly
* flatten input
* no array for locks
* removed expanded code
* expand constructor support
* types expanded
* use accessors
* sample attachment accessor
* nested output
* vec unfold
* comment added
* naming shortened
* converter, renamings
* process error as normal type
* zerror declared
* keyexpr default constructor
* accessor_fun
* full expand
* recursive expamd demo
* new api migration
* zbytes support callback error interface
* expand callbacks
* output kotlin one file per package
* flattened structure
* named callback parameters
* regenrate kotlin code
* generated files reformatted
* cargo fmt
* callback formatting
* no throws in generated code
* hello and query unwrap
* tests fixed
* query close in reply operations
* reply unwrap
* multiline long calls
* sample attributes added
* typed interface callback
* generated code insignificant change
* derfaults in error
* proxy interface
* accelerated onerror handler
* unfold encoding for acceleration
* renamings in builder
* refactor: Remove local zenoh-flat-jni subproject, consume as Maven dependency
- Remove :zenoh-flat-jni from gradle subprojects (moved to separate repo)
- Replace local source dependency with Maven: org.eclipse.zenoh:zenoh-flat-jni:1.9.0
- Remove Cargo build orchestration (buildZenohFlatJni, buildZenohFlatJNI, BuildMode)
- Remove build paths for zenoh-flat-jni libraries (now bundled in Maven JAR)
- Remove Cargo-related Gradle plugins and tasks
- Native libraries are now obtained from the zenoh-flat-jni Maven artifact
- Kotlin sources are now imported from the Maven JAR as a dependency
- Supports optional local development via: includeBuild("../zenoh-flat-jni")
- Simplifies zenoh-java build: Gradle now only compiles Kotlin, not Rust
Migration notes:
- zenoh-flat-jni is now at https://github.com/ZettaScaleLabs/zenoh-flat-jni
- Versions will be synchronized with Zenoh releases (1.9.0, etc.)
- For local development, clone both repos and use composite builds if needed
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* build: Enable Gradle composite build for local zenoh-flat-jni development
- Uncomment includeBuild for ../zenoh-flat-jni since it's now in the PREBINDGEN workspace
- zenoh-flat-jni is at /Users/milyin/PREBINDGEN/zenoh-flat-jni
- Gradle will prefer local build over Maven artifact when composite build is enabled
- Allows coordinated development of zenoh-java and zenoh-flat-jni
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: Remove zenoh-flat-jni directory from zenoh-java
The zenoh-flat-jni project has been moved to:
- GitHub: https://github.com/ZettaScaleLabs/zenoh-flat-jni
- Local workspace: /Users/milyin/PREBINDGEN/zenoh-flat-jni
zenoh-java now consumes zenoh-flat-jni via:
1. Maven artifact (org.eclipse.zenoh:zenoh-flat-jni:1.9.0) in production
2. Gradle composite build (../zenoh-flat-jni) for local development
Removing the local copy avoids duplication and confusion.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* api change ported
* serialize fixes
* loading in zenoh-flat-jni
* import predefined encodings as consts
* Use typed zenoh-flat-jni key expression overloads
* Install CI lint components for the pinned Rust toolchain
* Run flat JNI clippy without a tracked lockfile
* Preserve consuming Rust API semantics
* Limit rustfmt CI to the JNI crate
* Remove stale local task lock
* Use generated JNI receiver methods
* Pin receiver-style JNI bindings in CI
* Track de-prefixed zenoh-flat type names in JNI bindings
zenoh-flat's consolidated API dropped the Z prefix from its Rust types
(ZSample -> Sample, ...), and the generated callback interface names follow
the type name mechanically: ZSampleCallback -> SampleCallback etc. Rename the
four callback references in FlatCallbacks.kt (signatures are unchanged) and
follow the encoding id accessor rename (id -> getId).
Pin CI to the updated zenoh-flat-jni (receiver-style bindings regenerated
against prebindgen main and zenoh-flat main) and to zenoh-flat main.
Verified locally: jvmTest 101 tests, 0 failures against the composite build.
* Use derived JNI factory and getter names
zenoh-flat-jni now derives all regular method names from the flat crate's
identifiers (explicit .name() only where a clash forces it), so the internal
calls follow: JniKeyExpr.newTryFrom/newAutocanonize/newJoin/newConcat,
JniConfig.newFrom*, JniEncoding.newFromString/newWithSchema,
JniZBytes.newFromVec, zSession.getPeersZid/getRoutersZid. SDK public API
unchanged. CI pin bumped to the matching zenoh-flat-jni commit.
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…put override (#484) * Make Encoding and ZenohId.toString pure JVM values An encoding IS its (id, schema) pair — Zenoh's own representation; the textual form is derived from a fixed table. With the id<->name table already JVM-side (the generated ENCODING_* constants) and the companion zenoh-flat-jni change crossing Encoding by value in both directions, the SDK Encoding becomes a plain immutable value: no native handle, no native calls, no lazy caches, no locking, nothing to close. This also removes a native memory leak: received Sample/Query/ReplyError previously retained a cloned Encoding handle with no close path. - Encoding.kt: immutable (id, schema); from()/toString()/withSchema() implement Zenoh's exact conversion rules (incl. the custom-encoding name-preserving withSchema and the render-only CUSTOM<->"" table row); equality on (id, schema), matching Zenoh core. - ZenohId.toString: the little-endian lowercase-hex rule in pure Kotlin. - Callback/fromParts leaves follow the new (encId, encSchema) decomposition; outbound sites pass .id/.schema directly. - Correspondence tests (EncodingCorrespondenceTest, ZenohIdCorrespondenceTest) verify the pure implementations against the native ones across the whole predefined id range, parse/render edge shapes, and random ids — the contract for any JVM-side reimplementation of zenoh-flat API. One test caught and fixed a real divergence (custom-encoding withSchema). Pairs with ZettaScaleLabs/zenoh-flat-jni#4 (CI pin bumped). jvmTest: 105 tests, 0 failures. * Publisher default encoding at declare + PinnedEncoding per-put override Two encoding fast paths over the dual-arm expansion (prebindgen#80, zenoh-flat#3, zenoh-flat-jni#4): - The publisher's default encoding is set NATIVELY once at declare time (PublisherOptions.encoding -> declarePublisher's encoding arm); plain put(payload) passes absent (sel -1), so no encoding data crosses per call. Previously the JVM tier re-sent (id, schema) on every put. - PinnedEncoding (Encoding.pinned()) preallocates the native form; passing it in PutOptions/ReplyOptions/GetOptions crosses only a borrowed handle (native clone = Arc bump) — no schema-string traffic in hot publish loops. close() releases the handle (falls back to the plain (id, schema) arm); a finalizer backstops leaks. A PinnedEncoding equals its plain counterpart. - All outbound sites route through one Encoding?.forWire() helper emitting the selector tuple (-1 absent / 0 value / 1 pinned handle). Tests: PinnedEncodingTest (publisher-default applies natively end-to-end; pinned override reused across puts; post-close fallback), correspondence tests updated to the dual-arm newWithSchema. jvmTest: 107 tests, 0 failures. CI pins: zenoh-flat-jni cbbca40, zenoh-flat d792547. * Move encoding/zid logic to the shared bindings tier; drop pinning zenoh-flat-jni is the shared tier reused by both zenoh-java and zenoh-kotlin, so the pure-JVM conversion logic moves there (EncodingCodec, ZenohId.zidString — next to the generated constants they are built from). io.zenoh.bytes.Encoding shrinks to a thin facade: the established constants and API delegating to the shared codec, zero logic. Drop PinnedEncoding and the selector-tuple wire helper: with the publisher's default encoding set natively at declare time, the hot publish loop crosses no encoding data at all, and per-call overrides crossing (id, schema) cost only a short string decode — not worth a resource class in the public API. Encoding params revert to the plain (present, id, schema) crossing. jvmTest: 106 tests, 0 failures (correspondence suites now exercise the shared codec; PublisherEncodingTest verifies the native declare-time default + per-put override end-to-end). CI pins: zenoh-flat-jni 9bc155b, zenoh-flat main (6d22091, #3 merged).
…485) * Encoding: native handle where it is born, value where it is cheapest Minimize JNI crossings for every encoding flow (consumes zenoh-flat-jni PR #5): - Predefined constants stay VALUE-ONLY: a send carries just their id inside the send call itself — no native handle ever exists for them, no extra crossing. - Custom (schema-carrying) encodings create their native handle at construction — the one natural crossing — and every send after passes a bare jlong (borrowed and cloned natively, reusable forever). - Received encodings (sample/query/reply) arrive send-ready: the delivery decomposition now includes the owned handle, so the save-and-republish scenario never rebuilds the native value from its schema string. Handle release is GC-managed (EncodingCleaner in the shared tier); Encoding stays a plain non-closeable value with unchanged (id, schema) equality/rendering. Send call sites drive the generated encoding selector block through the internal Encoding?.jniSel/jniId/jniSchema/jniHandle helpers — one flat call, zero extra crossings in every case. New EncodingHandleTest covers the ownership model end-to-end; 110 jvm tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Lifecycle backstops moved into the gc_managed JNI tier zenoh-flat-jni's Encoding, Session, Publisher, Subscriber, Queryable, Querier, Scout, LivelinessToken and Query handles are now gc_managed (milyin/prebindgen#82): a shared Cleaner frees an unreachable handle whose owner never released it, with explicit close/undeclare settling the release ticket first. Delete the 8 deprecated-for-removal finalize() nets (JEP 421) — the JNI handle is its own backstop now — and drop Encoding's EncodingCleaner registration (superseded by the gc_managed class; the hand-written helper is deleted in zenoh-flat-jni). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * CI: bump zenoh-flat-jni pin to the merged encoding-handle commit The encoding selector wire shape and gc_managed lifecycles this branch consumes landed in ZettaScaleLabs/zenoh-flat-jni#5 (e802317); the old pin predates it, so CI compiled against the previous (present, id, schema) encoding signatures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Received preset encodings are value-only end to end zenoh-flat-jni#6 makes the delivered encoding handle CONDITIONAL on schema presence (binding-local encoding_if_schema behind prebindgen#84's field! leaf): a received preset re-sends through the id arm for free, so no per-message native handle — clone + Box + JVM wrapper + gc-Cleaner registration + Cleaner free — is ever materialized for it. Schema- carrying encodings keep the send-ready handle. Adapt the delivery plumbing: Sample.fromParts encH becomes nullable (Query's already was), the reply callback drops its non-null assertion, and EncodingHandleTest's preset round-trip flips to assert the received copy has NO handle — the new contract. CI pin bumped to the flat-jni#6 tip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * CI: bump zenoh-flat-jni pin to the #6 merge commit The previous pin was a branch commit deleted on merge; 1e040c9 is ZettaScaleLabs/zenoh-flat-jni#6 on main (conditional encoding handle, final fun!+sig! syntax). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…Encoding always (id, schema) (#489) * Received preset encodings are value-only end to end zenoh-flat-jni#6 makes the delivered encoding handle CONDITIONAL on schema presence (binding-local encoding_if_schema behind prebindgen#84's field! leaf): a received preset re-sends through the id arm for free, so no per-message native handle — clone + Box + JVM wrapper + gc-Cleaner registration + Cleaner free — is ever materialized for it. Schema- carrying encodings keep the send-ready handle. Adapt the delivery plumbing: Sample.fromParts encH becomes nullable (Query's already was), the reply callback drops its non-null assertion, and EncodingHandleTest's preset round-trip flips to assert the received copy has NO handle — the new contract. CI pin bumped to the flat-jni#6 tip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * CI: bump zenoh-flat-jni pin to the #6 merge commit The previous pin was a branch commit deleted on merge; 1e040c9 is ZettaScaleLabs/zenoh-flat-jni#6 on main (conditional encoding handle, final fun!+sig! syntax). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * KeyExpr: string-backed except declared Consumes zenoh-flat-jni's string-delivering KeyExpr output. Ownership model (the Encoding model transplanted): a native handle exists ONLY behind Session.declareKeyExpr — the single case zenoh attaches a wire declaration (the only state beyond the string, paying off only on send through the declaring session). Everything else is a plain string value: - tryFrom validates natively once and releases the probe handle; autocanonize reads the canonized string and releases; - RECEIVED keyexprs (sample/query/reply) arrive as ONE eager string — no native allocation, nothing to free; the old per-message handle never carried a declaration (zenoh's RX path builds declaration-less) so it bought nothing on re-send while costing an unfreeable alloc and a second getStr crossing for every string-reading consumer; - sends pick the string or handle selector arm via KeyExpr.jniSel/jniStr/jniHandle (cloneHandle for consuming params); - algebra ops use the declared handle or a transient probe; - undeclare demotes to string-backed; close() is meaningful only for declared instances (doc matches upstream again). 113 jvmTest green incl. the new KeyExprHandleTest (constructed/declared/ received ownership + saved-keyexpr re-publish). CI pin bumped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * CI: bump flat-jni pin to the gc_managed-KeyExpr commit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * examples: ping-pong exercises the receive-then-resend encoding path ZPing puts carry a custom schema-carrying encoding and verify (after warmup) that the echoed sample brings it back; ZPong retransmits the RECEIVED payload AND encoding — a pong that drops metadata is not a faithful echo, and the round trip now measures the real receive-then-resend path the send-ready Encoding handle was built for. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Encoding: received encodings are value-only (id, schema) Adapts to zenoh-flat-jni aecffac, which dropped the receive-side conditional handle leaf: the ping-pong A/B (ZPing/ZPong with a custom schema-carrying encoding, real payload+encoding retransmission) showed the per-receive handle lifecycle costs more than the schema re-decode it saves on resend (flat medians 68/68us handle vs 58/67us value-only at 8B/1KB, value-only never slower). Custom encodings still own a construction-born handle and send as a bare jlong; a saved received encoding re-sends through the (id, schema) value arm. Callback lambdas and Sample/Query.fromParts lose the encH leaf; EncodingHandleTest asserts value-only delivery; CI pin bumped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: bump zenoh-flat-jni pin to e4a8ed7 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * examples: restore upstream ZPing/ZPong The custom-encoding echo instrumentation was measurement scaffolding for the Encoding handle A/B (verdict shipped: received encodings are value-only); the examples keep their upstream form. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Every handle-owning class except ZBytes is now GC-backstopped (Config joined in zenoh-flat-jni PR#8), and the docs now say exactly what happens to forgotten instances: - Session: close() KDoc contradicted the code — close undeclares every live declaration, it does not leave them alive; class KDoc notes the GC backstop. - ZBytes: new lifecycle section — created ZBytes are plain values; received ones free their native buffer on first read; a received payload/attachment that is never read keeps it (deliberately NOT GC-managed: per-message Cleaner cost measured -23% thr @8B). - Publisher/Querier: session reference is weak — forgotten instances are undeclared by the GC backstop, not at session close only. - Subscriber/Queryable: session reference is strong — dropping the reference does NOT stop them; active until undeclare or session close. - Scout/LivelinessToken: 'dropped' now spelled out as GC-collected, non-deterministic; close for prompt effect. - KeyExpr (declared) / Config: backstop notes. CI pin bumped to flat-jni dbb1f8c (Config gc_managed, PR#8). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ls (#491) * Session.undeclare: detach the key-expr handle even when undeclare fails The generated wrapper consumes the handle even when the native undeclare errors (the Rust side takes it by value). Clearing keyExpr.handle only after a successful call left a dead handle attached on failure: every later operation selected the closed handle and failed instead of degrading to the string form. Detach in a finally; regression-tested by undeclaring through the wrong session. Found while porting zenoh-kotlin (eclipse-zenoh/zenoh-kotlin#668), which had inherited the same pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Record PR #491 in the transition table Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…entation (#492) * Parameters becomes a thin facade over the shared string-backed implementation The per-SDK map-backed parser duplicated zenoh-kotlin's and diverged from Rust: it percent-decoded values, rejected duplicated keys (throwing on attacker-controlled selector parameters in the queryable upcall), and normalized eagerly. io.zenoh.query.Parameters now delegates every operation to the shared io.zenoh.jni.query.Parameters (zenoh-flat-jni), a pure-Kotlin string-backed mirror of Rust's zenoh-protocol parameters.rs — construction is infallible on any input with zero JNI crossings. Behavior changes, all aligning with Rust: no percent-decoding; from(String) never throws; duplicated keys accepted with first-match-wins get (last wins in toMap, as Rust's HashMap conversion); trailing ';'/'='/'|' trimmed at construction; toString round-trips the stored string; equality is string equality. New ParametersCorrespondenceTest validates the shared implementation against the native parameters_get/insert/is_well_formed oracle (zenoh-flat#4) over edge shapes and 500 randomized inputs — it caught the trailing-separator trim rule. remove is correspondence-exempt: upstream zenoh's parameters::remove has an iterator-consumption bug (find advances past entries preceding the first match before filter runs, dropping them; removing an absent key erases everything); the shared implementation follows the documented 'preserving the insertion order' contract instead, and nativeRemoveBugCanary pins the buggy native behavior so the exemption is removed when upstream fixes it. Also folds in the RX hardening that motivated the unification: queryCallbackOf frees the owned native leaves and finalizes the query if decomposition ever throws, and QueryParametersTest sends a=1;a=2;bad=%zz through the raw bindings end to end. CI pins bump to zenoh-flat#4 and zenoh-flat-jni#9. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Extend parameters correspondence to the full API; reframe the adaptation zenoh-flat's parameters functions are regular API (values, contains_key and extend now declared too); the JVM runs the same semantics in pure Kotlin because crossing JNI per string operation is expensive — a JNI peculiarity, not a zenoh-flat design choice. Correspondence now covers get/values/ containsKey/insert/extend/isWellFormed over the edge shapes and 500 randomized inputs; comments and test names updated to say 'native implementation' (remove stays exempt behind nativeRemoveBugCanary until eclipse-zenoh/zenoh#2687 lands). CI pins bump to the reworked zenoh-flat and zenoh-flat-jni heads. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: pin zenoh-flat to merged main (parameters API, zenoh-flat#4) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Migrate to the split error-handler API (zenoh-flat-jni #45) prebindgen split the generated JNI error callback into two channels: the binding `JniErrorHandler.run(je)` (unchanged) and the typed domain `ErrorHandler.run(message)` (`je` removed, called only on a domain `Err`). A fallible flat wrapper now takes both — `onBindingError` then `onError`. `JNIErrorHandlers.kt`: - `throwZError` becomes the 1-arg domain handler (`ErrorHandler { message -> throw ZError(message) }`). - `throwZError0` (the binding `JniErrorHandler`) is unchanged — it now doubles as the `onBindingError` of a fallible call. Call sites: every fallible flat call gains `throwZError0` before its `throwZError` (42 sites across Config/Session/KeyExpr/Liveliness/Publisher/ Querier/Query/Scout + one jvmTest), matching the wrapper's `(…, onBindingError, onError)` order. Infallible calls (`throwZError0`) and the binding-only `Encoding.newFromId { je -> … }` lambda are untouched. Builds against the local composite zenoh-flat-jni (split-error-handler); 120 jvmTest tests pass, including the domain-error paths (`configFailsWithIllFormatedYAMLTest`, `insertIllFormattedJson5ShouldFailTest`). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: repoint zenoh-flat-jni pin to the split regen + ownership marker The split error-handler regeneration (zenoh-flat-jni #10) plus the merged Kotlin ownership-marker fix (zenoh-flat-jni #11, 249fe9e on shared-parameters). CI pinned the pre-split 757cc6a, whose single-channel `ErrorHandler.run(je, message)` wrappers are incompatible with this branch's two-caller call sites; #11's marker is also required for the composite `cargo build` to regenerate at all (write_kotlin refuses a non-empty output root without it). Point CI at the merged commit so the composite build both matches the source and regenerates. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Serialize via the shared pure-Kotlin SerializationCodec (no JNI) Replace the per-element JNI serialization (serializeViaJNI) with the shared pure-Kotlin SerializationCodec. ZSerializer/ZDeserializer build a SerializationCodec.SerdeType from the Guava TypeToken's java.lang.reflect.Type (serdeTypeOfJava) and call the shared codec through the SAME throwZError0 error handler used for generated wrappers (the codec never throws — it invokes the handler, which throws ZError), so the hand-written serializer is wired exactly like a generated one. No duplication — the same codec zenoh-kotlin uses. Full jvmTest: 120 pass (the 10 ZBytes tests now run via pure Kotlin). The Type path's supported set is unchanged (signed/collection; Java can't express the unsigned/Pair/Triple types the KType path adds). Depends on the zenoh-flat-jni SerializationCodec commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: bump zenoh-flat-jni pin to the pure-Kotlin serializer commit The pure-Kotlin SerializationCodec this branch delegates to lives in zenoh-flat-jni#13 (db4fb2d). Point CI at it so the composite build has the shared codec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: repoint zenoh-flat-jni pin to the rebased serializer commit The zenoh-flat-jni serialization PR was rebased onto main (conflict resolution), changing the SerializationCodec commit SHA. Repoint CI at the current commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Drop correspondence tests moved down to zenoh-flat-jni The Parameters/Encoding/ZenohId correspondence tests moved into zenoh-flat-jni's own test suite (it now self-verifies its pure implementations against the native oracle, which relocated to the internal io.zenoh.jni.test package). SDK production is unaffected — it uses the pure io.zenoh.jni.query.Parameters and the generated Encoding/ZenohId handles, all unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: bump zenoh-flat-jni pin to the test-package/self-verify commit zenoh-flat-jni moved its native oracle to the internal io.zenoh.jni.test package and added self-verifying correspondence tests. Point CI at that commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci: bump zenoh-flat-jni pin to merged main (71736f2) zenoh-flat-jni #13 squash-merged to main as 71736f2. Re-pin from the pre-merge branch commit 2755c06 (an orphan once the pure-kotlin-serde branch is deleted) to the permanent main commit, which also carries the final merged SerializationCodec (strict UTF-8). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
zenoh-flat renamed part of its surface and moved several handle types to
value forms (ZettaScaleLabs/zenoh-flat-jni#16). Mostly mechanical, with one
change that reaches the public API.
## A reply timestamp needs a real originating-node id
`ReplyOptions.timeStamp` was an Apache commons-net `TimeStamp`: an NTP64
instant and nothing else. Zenoh's timestamp is the pair `(ntp64, id)`, and it
orders and de-duplicates on the pair — so the missing half was supplied by the
JNI layer, which built one with a **random** id:
Timestamp::new(NTP64(timestamp_ntp_64 as u64), ID::rand()) // zenoh-jni/src/query.rs:82
The time looked right while the id was untethered from any node. zenoh-flat
removed that (ZettaScaleLabs/zenoh-flat#47) and `query_reply_success` now takes
a whole `Timestamp`, so there is no longer anywhere for a fabricated id to
come from — which is the point.
`io.zenoh.time.Timestamp` is that pair, mirroring `zenoh::time::Timestamp`.
`ReplyOptions.timeStamp` and `Sample.timestamp` carry it instead of the
commons-net type. **This is source-breaking**: a caller now writes
options.setTimeStamp(Timestamp.ofNtp64(ntp64, session.info().zid()));
`ofNtp64` exists because the primary constructor takes `ULong`, which Java
cannot express; it takes the same 64 bits `TimeStamp(long)` does, so
`TimeStamp.getCurrentTime().ntpValue()` still feeds it. The id is the
replying session's — the reply does originate here — and `Sample.timestamp`
now surfaces the sender's id, which was previously discarded on receive.
The library no longer depends on commons-net; the tests and the ZQueryable
example still use it, as an NTP64 clock.
## Mechanical
* `keyexpr_get_str` -> `asStr`, `zbytes_as_bytes` -> `toBytes`.
* `session_get` takes a whole `Selector`, so the folded (key_expr, parameters)
pair is gone. A Selector holds an owned key-expr handle with no string arm,
so `KeyExpr.intoJniHandle()` materializes one for a string-backed key
expression — the slot trio cannot express this case.
* An encoding schema is raw bytes: zenoh transmits it verbatim and does not
require UTF-8. This SDK's `Encoding` carries a String, so it is encoded at
the boundary on send and decoded lossily on receive, rather than throwing on
a received message.
* A sample's source and a reply's replier arrive as whole `SourceInfo` /
`EntityGlobalId` values, so optionality lives on the value rather than on a
leading zid leaf.
* `config_new_from_json` is gone as an invented constructor (base zenoh has no
`from_json`). `Config.fromJson` parses via JSON5, of which JSON is a subset,
so every input accepted before still parses to the same config.
CI pins move to zenoh-flat 3f431b6b and zenoh-flat-jni 5e0ae509.
112 JVM tests pass; examples compile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ZettaScaleLabs/zenoh-flat-jni#16 merged as 498ba26, so the pin moves off the PR branch commit it was tracking. The squash merge's tree is byte-identical to the branch tip this was verified against, and 498ba26 pins zenoh-flat at the same 3f431b6b already pinned here, so nothing but the SHA changes. 112 JVM tests pass against it; examples compile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* CI: track zenoh-flat-jni onto the split prebindgen crates zenoh-flat-jni#23 replaces its single `prebindgen` dependency with the three crates the #371 split produced, so this workflow's two hardcoded per-crate rewrites no longer match anything: the path deps would survive into a checkout that has no sibling `../prebindgen`. One regex over every `../prebindgen/*` dep covers the split and whatever it grows next. The pinned checkouts also move from the stale ZettaScaleLabs forks (last pushed in July) to the eclipse-zenoh repositories the work actually lives in, at the two open PR heads. No Kotlin change: zenoh-flat-jni regenerates byte-identical Kotlin over the split, and all 112 jvmTest cases pass against it locally. * CI: drop the prebindgen path rewrite, it is published prebindgen 0.5.0 is on crates.io, so zenoh-flat-jni and zenoh-flat declare version constraints and nothing has to be rewritten before the build. Re-pin both checkouts to the PRs that make that switch. * CI: re-pin zenoh-flat to main, zenoh-flat-jni to zenoh-flat-jni#24 eclipse-zenoh/zenoh-flat#80 merged as 81feb94. zenoh-flat-jni#25 landed on the #24 branch rather than main, so that checkout tracks #24 now. * CI: re-pin zenoh-flat-jni to main eclipse-zenoh/zenoh-flat-jni#24 merged as 6f81eb8; both sibling checkouts now sit on their repositories' main.
…514) * Make the release resolve zenoh-flat-jni like any other consumer The release path on this branch was dead, not merely dated. bump-and-tag wrote to zenoh-jni/Cargo.toml, publish-jvm cross-built it and publish-android installed Rust Android targets - and zenoh-jni/ was removed when the SDK moved onto the generated bindings. The first job of a release would have failed. - settings.gradle.kts included ../zenoh-flat-jni unconditionally, so a release would have been built against whatever sat on the builder's disk. Now opt-in via -PuseLocalFlatJni=true; CI passes it explicitly because it checks the sibling out, and a release does not. - The dependency version moves to gradle.properties, so the release can bump it and a rehearsal can point at a snapshot without editing tracked source. - bump-and-tag bumps version.txt and that property, and refuses a -SNAPSHOT dependency outright: consumers do not configure the snapshot repository, and snapshots mutate and expire, so a release that depended on one would be broken for everyone but us. - The zenoh-version input becomes zenoh-flat-jni-version, which is the only upstream version this repository now chooses. - Both publish workflows drop their native toolchains; the libraries arrive inside the zenoh-flat-jni artifact. The unapplied rust-android-gradle plugin goes with them. Verified: the build fails to resolve without the flag (as a release should), succeeds with it, and the bump script rejects a snapshot and rewrites both files otherwise. * Document how zenoh-java is published Mirrors zenoh-flat-jni's guide in structure and intent, but the pipeline here is much smaller and the document says why: this repository builds no native code, so there is no matrix, no cross-compilation and no artifact to inspect - the libraries arrive inside the zenoh-flat-jni dependency. The concepts that guide already explains once - coordinates, staging, signing - are linked rather than restated. What is specific here is the ordering constraint, which is the question the transition keeps raising: zenoh-java cannot be released until the zenoh-flat-jni version it depends on is really on Central, because consumers do not configure the snapshot repository and snapshots mutate and expire. Rehearsals have no such constraint, and the document gives the ladder - local composite, snapshot dependency, snapshot publication - with what each one actually proves. Known gaps are stated rather than implied: the repaired release path has never run, there is no consumer test, and the Android NDK configuration is retained unverified. * Let a rehearsal resolve an unreleased zenoh-flat-jni The guide told the reader to add the snapshot repository 'for the duration of a rehearsal' - editing tracked source, which is the thing the rest of this change removes. That was not a procedure, it was a gap. The Central snapshot repository is now declared conditionally: present only when zenohFlatJniVersion ends in -SNAPSHOT, and content-filtered to that single module. A rehearsal names the version on the command line and nothing is edited; a release version never ends in -SNAPSHOT, so a release build cannot reach a mutable artifact even if someone tried. The guarantee is structural rather than procedural. Verified against the snapshot the zenoh-flat-jni rehearsal actually published: -PzenohFlatJniVersion=1.9.0-rc4-SNAPSHOT -> 112 tests pass, no composite build default (1.9.0) -> unresolvable, and the snapshot repository is absent from the searched locations * Let a rehearsal use a snapshot, and stop aborting on a no-op bump Three defects the review found in the release script and workflows, all of which would have stopped a release or a rehearsal outright. The snapshot rejection was unconditional, so the rehearsal this PR documents - pass the snapshot as zenoh-flat-jni-version - was refused before Gradle ran. It now applies only to live runs, which is where the reasoning actually holds: a release may not depend on a snapshot, a rehearsal is how the SDK is exercised before the binding exists at all. git commit on an unchanged gradle.properties exits non-zero, so supplying the version already configured aborted the release under set -e before tagging. It commits only when the file moved. Neither publish workflow ran Gradle at all unless maven_publish was on, so an unchecked rehearsal proved nothing while the guide claimed it proved the artifact assembles. Both now assemble and generate the POM unconditionally; only the upload is gated. The guide also claimed one binding artifact carries both desktop and Android natives. It does not - they are separate coordinates, and the Android publication currently depends on the desktop one, so Android consumers get no jni/<abi>/ libraries. Recorded as a defect with the release blocked on it rather than described as working. * Stop telling users this repository builds native code The README described the build that was removed: publishing 'triggers the compilation of Zenoh-JNI in release', the Android section required an NDK and four Rust targets, and a warning said the result was tied to the builder's rustup target. None of that is true now - the libraries arrive inside the zenoh-flat-jni artifacts, cross-compiled elsewhere, which is precisely why the result is *not* machine-specific. Also fixes coordinates that were wrong independently of this work: the Android dependency example named zenoh-kotlin-android in the zenoh-java README, and the JVM examples used zenoh-java-jvm, which is not what this repository publishes. * Give each publication the binding artifact it needs The Android POM named the desktop coordinate, so an Android consumer received an artifact with six desktop libraries and no jni/<abi>/ - it could not load anything. The build now selects zenoh-flat-jni-android when -Pandroid=true and zenoh-flat-jni otherwise, and the conditional snapshot repository serves both modules. Verified by generating both POMs, which turns out not to need an Android SDK even though building the AAR does: jvm/pom-default.xml -> zenoh-flat-jni androidRelease/pom-default.xml -> zenoh-flat-jni-android The selection is build-wide rather than per source set because commonMain references the generated classes, and Kotlin Multiplatform cannot see a dependency declared only in platform source sets. That is also the reason the two publications cannot come from one Gradle invocation, so the atomicity gap stays - now with its cause recorded and the upstream fix named: variant-aware module metadata in zenoh-flat-jni would collapse the two coordinates into one. Also fixes the javadoc JAR, which shipped empty: it packaged build/dokka/html, which Dokka never writes. Pointed at dokka/javadoc, with a check that fails the build rather than publishing 25 bytes again - now 780 KB across 172 files. README: Rust is no longer a prerequisite, the Android section no longer claims to build an NDK library, jvmTest no longer 'compiles the native library in debug mode', and the examples use 1.9.0, which resolves, instead of 1.1.1, which 404s. * One dependency, one invocation, one staging repository The payoff of the zenoh-flat-jni KMP change. commonMain declared the desktop or Android coordinate depending on a build-wide -Pandroid flag, because the two were unrelated coordinates and Kotlin Multiplatform cannot see a dependency declared only in the platform source sets. That forced the JVM and Android publications into separate Gradle invocations, hence separate staging repositories: a live release could make one coordinate public and then fail on the other. zenoh-flat-jni now publishes a root coordinate with module metadata, so one declaration resolves per target. The flavour selection goes, and with it the reason the publications were split - publish-jvm.yml and publish-android.yml merge into one publish.yml whose single Gradle invocation produces both and is followed by one closeAndRelease. Verified against the KMP snapshot 1.9.0-rc8-SNAPSHOT: the zenoh-java POM resolves zenoh-flat-jni-jvm and the zenoh-java-android POM zenoh-flat-jni-android, from the same single declaration; one invocation publishes both to mavenLocal; 112 tests pass. * Repoint the callers at the merged publish workflow Merging publish-jvm.yml and publish-android.yml left three references to the deleted files: two snapshot-publish jobs in ci.yml and the needs list of release.yml's publish-github. GitHub rejects the whole workflow when a reference does not resolve, so CI failed at startup with no jobs at all - which is why the PR showed a single check. The release.yml one is instructive: my edit replaced 'needs: [tag, publish-jvm, publish-android]', but the file said '[tag, publish-android, publish-jvm]'. Different order, no match, no error - the replacement simply did nothing. The two snapshot jobs become one, for the same reason the release publishes once: a single Gradle invocation produces both publications. Added a check that every './.github/workflows/...' reference and every 'needs' entry resolves, and ran it over all six files. * Correct PUBLISHING.md for the merged publish job and KMP resolution - the pipeline section still named publish-jvm/publish-android - the snapshot-repository snippet still showed includeModule, which under the KMP dependency would hide zenoh-flat-jni-jvm/-android and fail resolution - state that zenoh-java itself publishes two plain coordinates and why the workflow names its publication tasks rather than publishing all of them * Restore the documentation deployment in the release sequence Merging publish-jvm/publish-android into one publish job dropped the publish-dokka call with them, orphaning publish-dokka.yml and leaving the gh-pages API site README.md links to unmaintained. Restore it after publish, with the contents:write the gh-pages push needs.
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: milyin <1909657+milyin@users.noreply.github.com>
Review of the branch as of
|
Two files on this branch describe a repository that no longer exists. ZENOH_FLAT_TRANSITION.md was the integration branch's own bookkeeping — an architecture sketch and a table of the constituent PRs. The table stopped at #484 and its links still pointed at the ZettaScaleLabs forks the work has since left, so it now misinforms rather than informs; the same content, kept current, lives in the #482 description. It was always meant to go when the branch merges. rust-toolchain.toml pinned 1.97.1 for a repository that builds no Rust. CI installs its components explicitly (`--toolchain 1.93.0`) and runs every cargo command inside the sibling zenoh-flat-jni checkout, where that repository's own toolchain file applies, so this pin selects nothing and can only mislead whoever reads it next. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Review of
|
The lazy materialization in ZBytes is double-checked locking: the fast path reads `eager` outside the monitor while the slow path writes it under it. Without volatile, a reader observing the non-null reference has no happens-before edge to the copy that filled the array. Mark `eager` volatile. It moves out of the primary constructor because @volatile targets fields. The synchronized slow path stays: it is what guarantees exactly one thread copies out of and closes the native handle. `handle` stays non-volatile — it is only touched inside the monitor. Closes #516
Both SDKs are being rebuilt on the generated zenoh-flat-jni bindings (eclipse-zenoh/zenoh-java#482, eclipse-zenoh/zenoh-kotlin#669). On those branches the in-repo `zenoh-jni` crate is gone, and with it rust-toolchain.toml and every Cargo manifest - the SDKs carry no Rust at all. Once that lands on main, their legs of this matrix fail at the toolchain step, on a rust-toolchain.toml that no longer exists. They are not losing the ABI alignment the sync exists for, they are inheriting it one level down: zenoh-flat and zenoh-flat-jni, added above, reach zenoh for them. Their departure also takes the crate-path workaround with it. It existed solely because those two kept their manifest under zenoh-jni/; every remaining dependant has it at the toplevel, so the path is just `.`.
* CI: pin zenoh-flat-jni to the timestamp node-id fix zenoh-flat carried a timestamp's node id trimmed to its significant bytes while `ZenohId` carries the full zero-padded width, so a received sample's timestamp compared unequal to the one that stamped the reply whenever the session identifier had a high-order zero byte - about once in 256 sessions, both sides rendering identically. `QueryableTest` asserts exactly that equality, and the same flake was observed in zenoh-kotlin. Fixed in eclipse-zenoh/zenoh-flat#86 and picked up by eclipse-zenoh/zenoh-flat-jni#35, which this pins. Drop the zenoh-flat checkout: nothing reads it, since zenoh-flat-jni resolves zenoh-flat from git and its Cargo.lock is the rev that decides - a pin that pinned nothing while reading as if it did. * CI: stop rebuilding and re-linting zenoh-flat-jni here This repo pinned the Rust toolchain at 1.93.0 to add rustfmt and clippy to it, then ran `cargo fmt --check`, clippy, `cargo test --no-default-features` and `cargo build` inside the zenoh-flat-jni checkout. zenoh-flat-jni pins its own toolchain in rust-toolchain.toml, so the components landed on one toolchain while the checks ran on another - `cargo fmt` failed with "'cargo-fmt' is not installed" as soon as that pin moved to 1.97.1. Rather than chase the version, drop the checks: they are zenoh-flat-jni's own CI, run on three platforms for the very commit pinned here, and its format check uses an import-granularity config this bare `cargo fmt` never applied - so the two were not even the same check. `cargo build` goes too. The composite build's test task already depends on zenoh-flat-jni's native build, so Gradle drives cargo; verified by deleting the built dylib and running `jvmTest`, which rebuilt it and passed all 112 tests. What remains is `rustup show` in the zenoh-flat-jni directory, which installs whatever toolchain that repo pins, so this repo never names a version its dependency is free to change. * CI: pin zenoh-flat-jni to the merged commit The pin named the PR branch commit, which is reachable but not on main. eclipse-zenoh/zenoh-flat-jni#35 has since merged as e75529c with an identical tree, so this only makes the pin name a commit that main actually carries. * CI: track zenoh-flat-jni main instead of a pinned commit A pinned SHA has to be hand-edited for every upstream fix, and that hop - zenoh-flat-jni to this SDK - is the one no bot covers. The timestamp node-id flake is what that costs: the fix sat in zenoh-flat for a day while CI kept testing the commit the pin named. Below zenoh-flat-jni the chain is automatic (eclipse-zenoh/ci#465 puts it and zenoh-flat on the lockfile sync), so following its default branch makes the whole chain automatic. The trade is deliberate: a run is no longer reproducible from this repository's commit alone, and a broken zenoh-flat-jni main breaks CI here - which, for a branch whose entire purpose is to track those bindings, is the signal we want.
Umbrella PR for rebuilding zenoh-java on top of the generated JNI/Kotlin bindings
(
zenoh-flat-jni, produced by prebindgen fromzenoh-flat), replacing the hand-written
zenoh-jniRust crate and theio.zenoh.jniadapter layer on top of it. This is thezenoh-java counterpart of eclipse-zenoh/zenoh-kotlin#669 — both SDKs converge on the
same shared bindings tier:
The
zenoh-jnicrate (18 files, ~7.8k lines) and everyJNI*.ktadapter are deleted;this repo keeps the public Java-facing SDK plus three small adapter files
(
FlatCallbacks.kt,JNIErrorHandlers.kt,JavaTypeSerde.kt). No Rust is built hereany more.
Throughput
Receiver-style generated bindings (#481), release benchmark, subscriber median msg/s,
8 rounds × 100,000 messages per point:
mainError model
The generated bindings never throw across JNI: a fallible wrapper takes trailing
error-sink callbacks and returns the sink's value on failure. zenoh-java's public API
is exception-based, so the two shared handlers in
JNIErrorHandlers.ktthrowZErrorstraight from the sink and the SDK needs no
try/catchlayer at all.The protocol has two independent channels:
onBindingError(JniErrorHandler—binding-layer failures such as UTF-8 decode or a closed handle) and
onError(the typeddomain
ErrorHandler, carrying the decomposed zenoh message); an infallible wrappertakes only the first. Both handlers bind the generated interface's
out RtoNothing,so one instance each satisfies every wrapper regardless of return type.
Callbacks are value-decomposed: a
Sample,Query,HelloorReplyarrives asits leaves in one JNI crossing, with no per-field accessor calls.
Encoding and key expressions as values
Encodingis its(id, schema)pair — a plain immutable JVM value. The id↔nametable and Zenoh's parse/render rules live in the shared tier (
EncodingCodecinzenoh-flat-jni, correspondence-tested against the native implementation), leaving a thin
facade here. This removed a per-message native leak.
Sends are shaped to minimize JNI crossings: the predefined constants are value-only (a
send carries just their id, inside the send call); a custom schema-carrying encoding is
born with a native handle at construction — the one crossing — and re-sends that handle
as a bare
jlong; a publisher's default encoding is set natively at declare, so plainputs carry no encoding data at all. Received encodings are always value-only, since
delivering a handle per message measurably cost more than the schema re-decode it saved.
Symmetrically, a
KeyExprholds a native handle only when it was declared on asession; every other key expression is a validated string, rebuilt Rust-side inside the
call that needs it. Handle release is GC-managed by a shared
Cleanerin the generatedtier.
Serialization
zSerialize/zDeserializeno longer cross JNI.JavaTypeSerde.ktmaps a GuavaTypeToken'sjava.lang.reflect.Typeto aSerializationCodec.SerdeTypeand theshared pure-Kotlin codec in zenoh-flat-jni does the encoding — byte-identical to the
native serializer (correspondence-tested upstream). Kotlin-only unsigned value classes
and
Pair/Tripleerase in Java reflection and remain unexpressible here, as before.Public API changes
Source-breaking, all tracking zenoh/zenoh-flat semantics:
Timestamp—ReplyOptions.timeStampandSample.timestampcarryio.zenoh.time.Timestamp(the(ntp64, id)pair, mirroringzenoh::time::Timestamp)instead of commons-net's
TimeStamp. The old JNI layer supplied the missing half withID::rand(); a reply now carries the replying session's real id, and a received samplesurfaces the sender's, previously discarded. Callers write
options.setTimeStamp(Timestamp.ofNtp64(ntp64, session.info().zid())). commons-netdrops out of the library's dependencies (tests and the
ZQueryableexample still useit as an NTP64 clock).
Parameters— a thin facade over the shared string-backed implementation(Rust
parameters.rssemantics): values are no longer percent-decoded, parsing isinfallible, duplicate keys are accepted with first-match-wins
get, andtoStringround-trips verbatim. Correspondence-tested against the native oracle.
Config.fromJsonparses via JSON5 (of which JSON is a subset — every inputaccepted before parses to the same config); the invented
config_new_from_jsonentrypoint is gone.
SourceInfo/EntityGlobalIdvalues, so optionality lives on the value rather than on a leading zidleaf.
decoded lossily on receive, rather than throwing on a received message.
Build, CI and release
cargoBuildwiring in this repo; thenative library ships with
org.eclipse.zenoh:zenoh-flat-jni.gradle.properties(
zenohFlatJniVersion).-PuseLocalFlatJni=truesubstitutes a sibling checkoutthrough a Gradle composite build for coordinated development; it is off by default and
must stay off for a release.
publish-jvm.ymlandpublish-android.ymlare merged into onepublish.yml, so bothcoordinates come from a single Gradle invocation and share one staging repository —
released together or not at all.
bump-and-tag.bashknows the zenoh-flat-jni versionand refuses a snapshot on a live run. See
PUBLISHING.md.zenoh-flat-jni/zenoh-flatat pinned commits, builds throughthe composite build, and gates on the linked crate's fmt/clippy/feature-leak checks;
prebindgenresolves from crates.io. Pins are bumped as the upstream PRs land.Constituent PRs
Encoding/ZenohId.toStringas pure JVM values over the sharedEncodingCodec; publisher default encoding set natively at declareKeyExprstring-backed except declared,Encodingalways(id, schema)Session.undeclaredetaches the key-expr handle even when the native undeclare failsParametersbecomes a facade over the shared string-backed implementationSerializationCodec(no JNI)93c6f05Process
This PR stays draft while the transition is in progress. Individual changes land as
reviewable PRs targeting the
zenoh-flat-transitionbranch; this PR accumulates themand merges to
mainas a whole. Upstream companion PRs (prebindgen → zenoh-flat →zenoh-flat-jni) merge first; the zenoh-java PR then pins the exact upstream commits in CI
and merges once green. This description is the branch's living record — the
ZENOH_FLAT_TRANSITION.mdbookkeeping file it replaced is removed by #515.🤖 Generated with Claude Code