Skip to content

Commit f2f9b6f

Browse files
committed
zbytes support callback error interface
1 parent 43638af commit f2f9b6f

4 files changed

Lines changed: 39 additions & 14 deletions

File tree

zenoh-flat-jni/kotlin/io/zenoh/jni/bytes/JNIBytes.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import java.lang.reflect.Type
2424
// class <clinit> runs before either external fn below is invoked.
2525
private val ensureLoaded = ZenohLoad
2626

27-
internal external fun serializeViaJNI(any: Any, type: Type): ByteArray
27+
// `onError` mirrors the generated wrappers' error callback: on a serialization
28+
// failure the native side invokes it with the message (the binding-error `je`
29+
// arity), and the handler throws — no direct throw from native code.
30+
internal external fun serializeViaJNI(any: Any, type: Type, onError: (String?) -> ByteArray): ByteArray
2831

29-
internal external fun deserializeViaJNI(bytes: ByteArray, type: Type): Any
32+
internal external fun deserializeViaJNI(bytes: ByteArray, type: Type, onError: (String?) -> Any): Any

zenoh-flat-jni/src/zbytes.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,32 @@ use zenoh_ext::{VarInt, ZDeserializeError, ZDeserializer, ZSerializer};
2323

2424
type JResult<T> = core::result::Result<T, JniBindingError<()>>;
2525

26-
/// Throw the SDK's `io.zenoh.exceptions.ZError` with the error's message. This
27-
/// hand-written JNI surface (ZSerializer/ZDeserializer) throws directly —
28-
/// matching the canonical model where every error surfaces as `ZError` (the
29-
/// generated wrappers reach it via their `onError` callback; this hand-written
30-
/// path throws it straight, so callers no longer need an exception-bridge).
31-
/// `ZError(message: String?)` compiles to a `(Ljava/lang/String;)V` ctor.
32-
fn throw_jni_error(env: &mut JNIEnv, err: &impl core::fmt::Display) {
33-
let _ = env.throw_new("io/zenoh/exceptions/ZError", err.to_string());
26+
/// Deliver an error to the foreign `onError` callback — the SAME model the
27+
/// generated wrappers use (no direct `throw` from native). `on_error` is a
28+
/// Kotlin `(je: String?) -> R` function (the binding-error arity); we invoke it
29+
/// with the message via the erased `invoke`. The handler throws `ZError`, so a
30+
/// JVM exception is pending when we return the sentinel. (This hand-written
31+
/// surface holds no native handle locks, so invoking the throwing callback
32+
/// straight from the upcall is safe — no lock is held across the throw.)
33+
fn signal_error(env: &mut JNIEnv, on_error: &JObject, err: &impl core::fmt::Display) {
34+
// If a JVM exception is already pending (a Java upcall threw), let it
35+
// propagate untouched — do NOT invoke the callback over it (and do not
36+
// clear it): the pending exception surfaces when we return the sentinel.
37+
if env.exception_check().unwrap_or(false) {
38+
return;
39+
}
40+
let je: JObject = match env.new_string(err.to_string()) {
41+
Ok(s) => s.into(),
42+
Err(_) => JObject::null(),
43+
};
44+
// The callback throws `ZError`, leaving a pending exception; we ignore the
45+
// `Err` and return so it propagates.
46+
let _ = env.call_method(
47+
on_error,
48+
"invoke",
49+
"(Ljava/lang/Object;)Ljava/lang/Object;",
50+
&[JValue::Object(&je)],
51+
);
3452
}
3553

3654
/// Helper function to convert a JByteArray into a Vec<u8>.
@@ -190,6 +208,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_serializeViaJNI(
190208
_class: JClass,
191209
any: JObject,
192210
token_type: JObject,
211+
on_error: JObject,
193212
) -> jobject {
194213
|| -> JResult<jobject> {
195214
let mut serializer = ZSerializer::new();
@@ -201,7 +220,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_serializeViaJNI(
201220
Ok(byte_array.as_raw())
202221
}()
203222
.unwrap_or_else(|err| {
204-
throw_jni_error(&mut env, &err);
223+
signal_error(&mut env, &on_error, &err);
205224
JObject::default().as_raw()
206225
})
207226
}
@@ -332,6 +351,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_deserializeViaJNI(
332351
_class: JClass,
333352
bytes: JByteArray,
334353
jtype: JObject,
354+
on_error: JObject,
335355
) -> jobject {
336356
|| -> JResult<jobject> {
337357
let decoded_bytes: Vec<u8> = decode_byte_array(&env, bytes)?;
@@ -345,7 +365,7 @@ pub extern "C" fn Java_io_zenoh_jni_bytes_JNIBytes_deserializeViaJNI(
345365
Ok(obj)
346366
}()
347367
.unwrap_or_else(|err| {
348-
throw_jni_error(&mut env, &err);
368+
signal_error(&mut env, &on_error, &err);
349369
JObject::default().as_raw()
350370
})
351371
}

zenoh-java/src/commonMain/kotlin/io/zenoh/ext/ZDeserializer.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package io.zenoh.ext
1717
import com.google.common.reflect.TypeToken
1818
import io.zenoh.bytes.IntoZBytes
1919
import io.zenoh.bytes.ZBytes
20+
import io.zenoh.exceptions.throwZError0
2021
import io.zenoh.jni.bytes.deserializeViaJNI
2122

2223
/**
@@ -106,6 +107,6 @@ abstract class ZDeserializer<T>: TypeToken<T>() {
106107
*/
107108
fun deserialize(zbytes: IntoZBytes): T {
108109
@Suppress("UNCHECKED_CAST")
109-
return deserializeViaJNI(zbytes.into().bytes, this.type) as T
110+
return deserializeViaJNI(zbytes.into().bytes, this.type, throwZError0) as T
110111
}
111112
}

zenoh-java/src/commonMain/kotlin/io/zenoh/ext/ZSerializer.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package io.zenoh.ext
1616

1717
import com.google.common.reflect.TypeToken
1818
import io.zenoh.bytes.ZBytes
19+
import io.zenoh.exceptions.throwZError0
1920
import io.zenoh.jni.bytes.serializeViaJNI
2021

2122
/**
@@ -104,6 +105,6 @@ abstract class ZSerializer<T>: TypeToken<T>() {
104105
* Serialize [t] into a [ZBytes].
105106
*/
106107
fun serialize(t: T): ZBytes {
107-
return ZBytes.from(serializeViaJNI(t as Any, this.type))
108+
return ZBytes.from(serializeViaJNI(t as Any, this.type, throwZError0))
108109
}
109110
}

0 commit comments

Comments
 (0)