diff --git a/examples/src/main/java/io/zenoh/ZPubThr.java b/examples/src/main/java/io/zenoh/ZPubThr.java index 366dd1c..1d1b42d 100644 --- a/examples/src/main/java/io/zenoh/ZPubThr.java +++ b/examples/src/main/java/io/zenoh/ZPubThr.java @@ -16,9 +16,8 @@ import io.zenoh.exceptions.ZenohException; import io.zenoh.keyexpr.KeyExpr; +import io.zenoh.prelude.CongestionControl; import io.zenoh.prelude.Encoding; -import io.zenoh.prelude.KnownEncoding; -import io.zenoh.publication.CongestionControl; import io.zenoh.publication.Publisher; import io.zenoh.value.Value; @@ -30,7 +29,7 @@ public static void main(String[] args) throws ZenohException { for (int i = 0; i < size; i++) { data[i] = (byte) (i % 10); } - Value value = new Value(data, new Encoding(KnownEncoding.EMPTY)); + Value value = new Value(data, new Encoding(Encoding.ID.ZENOH_BYTES, null)); try (Session session = Session.open()) { try (KeyExpr keyExpr = KeyExpr.tryFrom("test/thr")) { try (Publisher publisher = session.declarePublisher(keyExpr).congestionControl(CongestionControl.BLOCK).res()) { diff --git a/examples/src/main/java/io/zenoh/ZPut.java b/examples/src/main/java/io/zenoh/ZPut.java index ba1196b..60317b8 100644 --- a/examples/src/main/java/io/zenoh/ZPut.java +++ b/examples/src/main/java/io/zenoh/ZPut.java @@ -17,8 +17,8 @@ import io.zenoh.exceptions.ZenohException; import io.zenoh.keyexpr.KeyExpr; import io.zenoh.prelude.SampleKind; -import io.zenoh.publication.CongestionControl; -import io.zenoh.publication.Priority; +import io.zenoh.prelude.CongestionControl; +import io.zenoh.prelude.Priority; public class ZPut { public static void main(String[] args) throws ZenohException { @@ -29,7 +29,6 @@ public static void main(String[] args) throws ZenohException { session.put(keyExpr, value) .congestionControl(CongestionControl.BLOCK) .priority(Priority.REALTIME) - .kind(SampleKind.PUT) .res(); System.out.println("Putting Data ('" + keyExpr + "': '" + value + "')..."); } diff --git a/examples/src/main/java/io/zenoh/ZQueryable.java b/examples/src/main/java/io/zenoh/ZQueryable.java index 6a710a9..c5a9c87 100644 --- a/examples/src/main/java/io/zenoh/ZQueryable.java +++ b/examples/src/main/java/io/zenoh/ZQueryable.java @@ -51,7 +51,7 @@ private static void handleRequests(BlockingQueue> receiver, KeyE String valueInfo = query.getValue() != null ? " with value '" + query.getValue() + "'" : ""; System.out.println(">> [Queryable] Received Query '" + query.getSelector() + "'" + valueInfo); try { - query.reply(keyExpr).success("Queryable from Java!").withKind(SampleKind.PUT).withTimeStamp(TimeStamp.getCurrentTime()).res(); + query.reply(keyExpr).success("Queryable from Java!").timestamp(TimeStamp.getCurrentTime()).res(); } catch (Exception e) { System.out.println(">> [Queryable] Error sending reply: " + e); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 743f7cd..7897a24 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.72.0" +channel = "1.75.0" diff --git a/version.txt b/version.txt index 19a5fa4..835b796 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.11.0-dev \ No newline at end of file +1.0.0-dev \ No newline at end of file diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt index 719199d..c5e50d6 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt @@ -19,13 +19,13 @@ import io.zenoh.exceptions.ZenohException import io.zenoh.handlers.Callback import io.zenoh.jni.JNISession import io.zenoh.keyexpr.KeyExpr +import io.zenoh.prelude.QoS import io.zenoh.publication.Delete import io.zenoh.publication.Publisher import io.zenoh.publication.Put import io.zenoh.query.* import io.zenoh.queryable.Query import io.zenoh.queryable.Queryable -import io.zenoh.sample.Attachment import io.zenoh.sample.Sample import io.zenoh.selector.Selector import io.zenoh.subscriber.Reliability @@ -104,6 +104,7 @@ class Session private constructor(private val config: Config) : AutoCloseable { jniSession = null } + @Suppress("removal") protected fun finalize() { jniSession?.close() } @@ -355,10 +356,10 @@ class Session private constructor(private val config: Config) : AutoCloseable { } @Throws(SessionException::class) - internal fun resolvePublisher(builder: Publisher.Builder): Publisher { + internal fun resolvePublisher(keyExpr: KeyExpr, qos: QoS): Publisher { return jniSession?.run { - declarePublisher(builder) - } ?: throw (sessionClosedException) + declarePublisher(keyExpr, qos) + } ?: throw(sessionClosedException) } @Throws(ZenohException::class) @@ -389,7 +390,7 @@ class Session private constructor(private val config: Config) : AutoCloseable { target: QueryTarget, consolidation: ConsolidationMode, value: Value?, - attachment: Attachment?, + attachment: ByteArray?, ): R? { if (jniSession == null) { throw sessionClosedException @@ -404,7 +405,7 @@ class Session private constructor(private val config: Config) : AutoCloseable { @Throws(ZenohException::class) internal fun resolveDelete(keyExpr: KeyExpr, delete: Delete) { - jniSession?.run { performPut(keyExpr, delete) } + jniSession?.run { performDelete(keyExpr, delete) } } /** Launches the session through the jni session, returning the [Session] on success. */ diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt index feea7a9..dd06cda 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt @@ -20,4 +20,4 @@ package io.zenoh.exceptions * This type of exception is thrown from the native code when something goes wrong regarding the * communication between the Java/Kotlin layer and the native layer through the JNI. */ -class JNIException : ZenohException() +class JNIException(msg: String?) : ZenohException(msg) diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt index 2e21894..a8e62cc 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt @@ -24,68 +24,46 @@ internal class JNIKeyExpr(internal val ptr: Long) { @Throws(ZenohException::class) fun tryFrom(keyExpr: String): KeyExpr { Zenoh.load() // It may happen the zenoh library is not yet loaded when creating a key expression. - val keyExprPtr = tryFromViaJNI(keyExpr) - return KeyExpr(JNIKeyExpr(keyExprPtr)) + return KeyExpr(tryFromViaJNI(keyExpr)) } @Throws(ZenohException::class) fun autocanonize(keyExpr: String): KeyExpr { Zenoh.load() - val keyExprPtr = autocanonizeViaJNI(keyExpr) - return KeyExpr(JNIKeyExpr(keyExprPtr)) + return KeyExpr(autocanonizeViaJNI(keyExpr)) } - @Throws(ZenohException::class) - private external fun tryFromViaJNI(keyExpr: String): Long + fun intersects(keyExprA: KeyExpr, keyExprB: KeyExpr): Boolean = intersectsViaJNI( + keyExprA.jniKeyExpr?.ptr ?: 0, + keyExprA.keyExpr, + keyExprB.jniKeyExpr?.ptr ?: 0, + keyExprB.keyExpr + ) - @Throws(ZenohException::class) - private external fun autocanonizeViaJNI(keyExpr: String): Long - } + fun includes(keyExprA: KeyExpr, keyExprB: KeyExpr): Boolean = includesViaJNI( + keyExprA.jniKeyExpr?.ptr ?: 0, + keyExprA.keyExpr, + keyExprB.jniKeyExpr?.ptr ?: 0, + keyExprB.keyExpr + ) - override fun toString(): String { - return getStringValueViaJNI(ptr) - } + @Throws(Exception::class) + private external fun tryFromViaJNI(keyExpr: String): String - fun intersects(other: KeyExpr): Boolean { - if (other.jniKeyExpr == null) { - return false - } - return intersectsViaJNI(ptr, other.jniKeyExpr!!.ptr) - } + @Throws(Exception::class) + private external fun autocanonizeViaJNI(keyExpr: String): String - fun includes(other: KeyExpr): Boolean { - if (other.jniKeyExpr == null) { - return false - } - return includesViaJNI(ptr, other.jniKeyExpr!!.ptr) + @Throws(Exception::class) + private external fun intersectsViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Boolean + + @Throws(Exception::class) + private external fun includesViaJNI(ptrA: Long, keyExprA: String, ptrB: Long, keyExprB: String): Boolean } fun close() { freePtrViaJNI(ptr) } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as JNIKeyExpr - - return equalsViaJNI(ptr, other.ptr) - } - - override fun hashCode(): Int { - return ptr.hashCode() - } - - private external fun equalsViaJNI(ptrA: Long, ptrB: Long): Boolean - - private external fun intersectsViaJNI(ptrA: Long, ptrB: Long): Boolean - - private external fun includesViaJNI(ptrA: Long, ptrB: Long): Boolean - - @Throws(ZenohException::class) - private external fun getStringValueViaJNI(ptr: Long): String - /** Frees the underlying native KeyExpr. */ private external fun freePtrViaJNI(ptr: Long) } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt index d212e2b..b2eb478 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt @@ -14,39 +14,35 @@ package io.zenoh.jni -import io.zenoh.* import io.zenoh.exceptions.ZenohException -import io.zenoh.publication.CongestionControl -import io.zenoh.publication.Priority -import io.zenoh.sample.Attachment import io.zenoh.value.Value /** - * Adapter class to handle the interactions with Zenoh through JNI for a [Publisher]. + * Adapter class to handle the interactions with Zenoh through JNI for a [io.zenoh.publication.Publisher]. * * @property ptr: raw pointer to the underlying native Publisher. */ internal class JNIPublisher(private val ptr: Long) { /** - * Put value through the publisher. + * Put operation. * * @param value The [Value] to be put. - * @param attachment Optional [Attachment]. + * @param attachment Optional attachment. */ @Throws(ZenohException::class) - fun put(value: Value, attachment: Attachment?) { - putViaJNI(value.payload, value.encoding.knownEncoding.ordinal, attachment?.let { encodeAttachment(it) }, ptr) + fun put(value: Value, attachment: ByteArray?) { + putViaJNI(value.payload, value.encoding.id.ordinal, value.encoding.schema, attachment, ptr) } /** * Delete operation. * - * @param attachment Optional [Attachment]. + * @param attachment Optional attachment. */ @Throws(ZenohException::class) - fun delete(attachment: Attachment?) { - deleteViaJNI(attachment?.let { encodeAttachment(it) }, ptr) + fun delete(attachment: ByteArray?) { + deleteViaJNI(attachment, ptr) } /** @@ -58,63 +54,14 @@ internal class JNIPublisher(private val ptr: Long) { freePtrViaJNI(ptr) } - /** - * Set the congestion control policy of the publisher. - * - * This function is not thread safe. - * - * @param congestionControl: The [CongestionControl] policy. - */ - @Throws(ZenohException::class) - fun setCongestionControl(congestionControl: CongestionControl) { - setCongestionControlViaJNI(congestionControl.ordinal, ptr) - } - - /** - * Set the priority policy of the publisher. - * - * This function is not thread safe. - * - * @param priority: The [Priority] policy. - */ - @Throws(ZenohException::class) - fun setPriority(priority: Priority) { - setPriorityViaJNI(priority.value, ptr) - } - - /** - * Set the congestion control policy of the publisher through JNI. - * - * This function is NOT thread safe. - * - * @param congestionControl The congestion control policy. - * @param ptr Pointer to the publisher. - */ - @Throws(ZenohException::class) - private external fun setCongestionControlViaJNI(congestionControl: Int, ptr: Long) - - /** - * Set the priority policy of the publisher through JNI. - * - * This function is NOT thread safe. - * - * @param priority The priority policy. - * @param ptr Pointer to the publisher. - */ - @Throws(ZenohException::class) - private external fun setPriorityViaJNI(priority: Int, ptr: Long) - - - /** Puts through the native Publisher. */ @Throws(ZenohException::class) private external fun putViaJNI( - valuePayload: ByteArray, valueEncoding: Int, encodedAttachment: ByteArray?, ptr: Long + valuePayload: ByteArray, encodingId: Int, encodingSchema: String?, attachment: ByteArray?, ptr: Long ) @Throws(ZenohException::class) - private external fun deleteViaJNI(encodedAttachment: ByteArray?, ptr: Long) + private external fun deleteViaJNI(attachment: ByteArray?, ptr: Long) - /** Frees the underlying native Publisher. */ private external fun freePtrViaJNI(ptr: Long) } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt index bf2d49b..20b8fb8 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt @@ -15,8 +15,11 @@ package io.zenoh.jni import io.zenoh.exceptions.ZenohException +import io.zenoh.keyexpr.KeyExpr +import io.zenoh.prelude.QoS import io.zenoh.sample.Sample import io.zenoh.value.Value +import org.apache.commons.net.ntp.TimeStamp /** * Adapter class for interacting with a Query using JNI. @@ -32,21 +35,41 @@ internal class JNIQuery(private val ptr: Long) { val timestampEnabled = sample.timestamp != null replySuccessViaJNI( ptr, - sample.keyExpr.jniKeyExpr!!.ptr, + sample.keyExpr.jniKeyExpr?.ptr ?: 0, + sample.keyExpr.keyExpr, sample.value.payload, - sample.value.encoding.knownEncoding.ordinal, - sample.kind.ordinal, + sample.value.encoding.id.ordinal, + sample.value.encoding.schema, timestampEnabled, if (timestampEnabled) sample.timestamp!!.ntpValue() else 0, - sample.attachment?.let { encodeAttachment(it) }, + sample.attachment, + sample.qos.express, + sample.qos.priority.value, + sample.qos.congestionControl.value ) } @Throws(ZenohException::class) fun replyError(errorValue: Value) { - replyErrorViaJNI(ptr, errorValue.payload, errorValue.encoding.knownEncoding.ordinal) + replyErrorViaJNI(ptr, errorValue.payload, errorValue.encoding.id.ordinal, errorValue.encoding.schema) } + @Throws(ZenohException::class) + fun replyDelete(keyExpr: KeyExpr, timestamp: TimeStamp?, attachment: ByteArray?, qos: QoS) { + val timestampEnabled = timestamp != null + replyDeleteViaJNI( + ptr, + keyExpr.jniKeyExpr?.ptr ?: 0, + keyExpr.keyExpr, + timestampEnabled, + if (timestampEnabled) timestamp!!.ntpValue() else 0, + attachment, + qos.express, + qos.priority.value, + qos.congestionControl.value + ) + } + fun close() { freePtrViaJNI(ptr) } @@ -54,13 +77,17 @@ internal class JNIQuery(private val ptr: Long) { @Throws(ZenohException::class) private external fun replySuccessViaJNI( queryPtr: Long, - keyExpr: Long, + keyExprPtr: Long, + keyExprString: String, valuePayload: ByteArray, - valueEncoding: Int, - sampleKind: Int, + valueEncodingId: Int, + valueEncodingSchema: String?, timestampEnabled: Boolean, timestampNtp64: Long, attachment: ByteArray?, + qosExpress: Boolean, + qosPriority: Int, + qosCongestionControl: Int, ) @Throws(ZenohException::class) @@ -68,6 +95,20 @@ internal class JNIQuery(private val ptr: Long) { queryPtr: Long, errorValuePayload: ByteArray, errorValueEncoding: Int, + encodingSchema: String?, + ) + + @Throws(ZenohException::class) + private external fun replyDeleteViaJNI( + queryPtr: Long, + keyExprPtr: Long, + keyExprString: String, + timestampEnabled: Boolean, + timestampNtp64: Long, + attachment: ByteArray?, + qosExpress: Boolean, + qosPriority: Int, + qosCongestionControl: Int, ) /** Frees the underlying native Query. */ diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt index db5c0df..e12b294 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt @@ -15,23 +15,24 @@ package io.zenoh.jni import io.zenoh.* +import io.zenoh.prelude.Encoding +import io.zenoh.prelude.Encoding.ID import io.zenoh.exceptions.SessionException import io.zenoh.exceptions.ZenohException import io.zenoh.handlers.Callback import io.zenoh.jni.callbacks.JNIOnCloseCallback -import io.zenoh.prelude.KnownEncoding import io.zenoh.jni.callbacks.JNIGetCallback import io.zenoh.jni.callbacks.JNIQueryableCallback import io.zenoh.jni.callbacks.JNISubscriberCallback import io.zenoh.keyexpr.KeyExpr -import io.zenoh.prelude.Encoding -import io.zenoh.prelude.SampleKind +import io.zenoh.prelude.* +import io.zenoh.protocol.ZenohID +import io.zenoh.publication.Delete import io.zenoh.publication.Publisher import io.zenoh.publication.Put import io.zenoh.query.* import io.zenoh.queryable.Query import io.zenoh.queryable.Queryable -import io.zenoh.sample.Attachment import io.zenoh.sample.Sample import io.zenoh.selector.Selector import io.zenoh.subscriber.Reliability @@ -47,32 +48,34 @@ internal class JNISession { /* Pointer to the underlying Rust zenoh session. */ private var sessionPtr: AtomicLong = AtomicLong(0) - @Throws(SessionException::class) + @Throws(ZenohException::class) fun open(config: Config) { config.jsonConfig?.let { jsonConfig -> sessionPtr.set(openSessionWithJsonConfigViaJNI(jsonConfig.toString())) } ?: run { - sessionPtr.set(openSessionViaJNI(config.path?.toString().orEmpty())) + sessionPtr.set(openSessionViaJNI(config.path?.toString())) } } + @Throws(ZenohException::class) fun close() { closeSessionViaJNI(sessionPtr.get()) } @Throws(ZenohException::class) - fun declarePublisher(builder: Publisher.Builder): Publisher { + fun declarePublisher(keyExpr: KeyExpr, qos: QoS): Publisher { val publisherRawPtr = declarePublisherViaJNI( - builder.keyExpr.jniKeyExpr!!.ptr, + keyExpr.jniKeyExpr?.ptr ?: 0, + keyExpr.keyExpr, sessionPtr.get(), - builder.congestionControl.ordinal, - builder.priority.value, + qos.congestionControl.value, + qos.priority.value, + qos.express ) return Publisher( - builder.keyExpr, + keyExpr, + qos, JNIPublisher(publisherRawPtr), - builder.congestionControl, - builder.priority, ) } @@ -81,20 +84,20 @@ internal class JNISession { keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R?, reliability: Reliability ): Subscriber { val subCallback = - JNISubscriberCallback { keyExprPtr, payload, encoding, kind, timestampNTP64, timestampIsValid, attachmentBytes -> + JNISubscriberCallback { keyExpr2, payload, encodingId, encodingSchema, kind, timestampNTP64, timestampIsValid, attachmentBytes, express: Boolean, priority: Int, congestionControl: Int -> val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val attachment = attachmentBytes.takeIf { it.isNotEmpty() }?.let { decodeAttachment(it) } val sample = Sample( - KeyExpr(JNIKeyExpr(keyExprPtr)), - Value(payload, Encoding(KnownEncoding.fromInt(encoding))), + KeyExpr(keyExpr2, null), + Value(payload, Encoding(ID.fromId(encodingId)!!, encodingSchema)), SampleKind.fromInt(kind), timestamp, - attachment + QoS(express, congestionControl, priority), + attachmentBytes ) callback.run(sample) } val subscriberRawPtr = declareSubscriberViaJNI( - keyExpr.jniKeyExpr!!.ptr, sessionPtr.get(), subCallback, onClose, reliability.ordinal + keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, sessionPtr.get(), subCallback, onClose, reliability.ordinal ) return Subscriber(keyExpr, receiver, JNISubscriber(subscriberRawPtr)) } @@ -104,17 +107,18 @@ internal class JNISession { keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R?, complete: Boolean ): Queryable { val queryCallback = - JNIQueryableCallback { keyExprPtr: Long, selectorParams: String, withValue: Boolean, payload: ByteArray?, encoding: Int, attachmentBytes: ByteArray, queryPtr: Long -> + JNIQueryableCallback { keyExprStr: String, selectorParams: String, withValue: Boolean, payload: ByteArray?, encodingId: Int, encodingSchema: String?, attachmentBytes: ByteArray?, queryPtr: Long -> val jniQuery = JNIQuery(queryPtr) - val keyExpression = KeyExpr(JNIKeyExpr(keyExprPtr)) - val selector = Selector(keyExpression, selectorParams) - val value: Value? = if (withValue) Value(payload!!, Encoding(KnownEncoding.fromInt(encoding))) else null - val decodedAttachment = attachmentBytes.takeIf { it.isNotEmpty() }?.let { decodeAttachment(it) } - val query = Query(keyExpression, selector, value, decodedAttachment, jniQuery) + val keyExpr2 = KeyExpr(keyExprStr, null) + val selector = Selector(keyExpr2, selectorParams) + val value: Value? = + if (withValue) Value(payload!!, Encoding(ID.fromId(encodingId)!!, encodingSchema)) else null + val query = Query(keyExpr2, selector, value, attachmentBytes, jniQuery) callback.run(query) } - val queryableRawPtr = - declareQueryableViaJNI(keyExpr.jniKeyExpr!!.ptr, sessionPtr.get(), queryCallback, onClose, complete) + val queryableRawPtr = declareQueryableViaJNI( + keyExpr.jniKeyExpr?.ptr ?: 0, keyExpr.keyExpr, sessionPtr.get(), queryCallback, onClose, complete + ) return Queryable(keyExpr, receiver, JNIQueryable(queryableRawPtr)) } @@ -128,140 +132,172 @@ internal class JNISession { target: QueryTarget, consolidation: ConsolidationMode, value: Value?, - attachment: Attachment? + attachment: ByteArray? ): R? { - val getCallback = - JNIGetCallback { replierId: String, success: Boolean, keyExprPtr: Long, payload: ByteArray, encoding: Int, kind: Int, timestampNTP64: Long, timestampIsValid: Boolean, attachmentBytes: ByteArray -> - if (success) { - val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null - val decodedAttachment = attachmentBytes.takeIf { it.isNotEmpty() }?.let { decodeAttachment(it) } - val sample = Sample( - KeyExpr(JNIKeyExpr(keyExprPtr)), - Value(payload, Encoding(KnownEncoding.fromInt(encoding))), - SampleKind.fromInt(kind), - timestamp, - decodedAttachment - ) - val reply = Reply.Success(replierId, sample) - callback.run(reply) - } else { - val reply = Reply.Error(replierId, Value(payload, Encoding(KnownEncoding.fromInt(encoding)))) - callback.run(reply) + val getCallback = JNIGetCallback { + replierId: String?, + success: Boolean, + keyExpr: String?, + payload: ByteArray, + encodingId: Int, + encodingSchema: String?, + kind: Int, + timestampNTP64: Long, + timestampIsValid: Boolean, + attachmentBytes: ByteArray?, + express: Boolean, + priority: Int, + congestionControl: Int, + -> + val reply: Reply + if (success) { + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + when (SampleKind.fromInt(kind)) { + SampleKind.PUT -> { + val sample = Sample( + KeyExpr(keyExpr!!, null), + Value(payload, Encoding(ID.fromId(encodingId)!!, encodingSchema)), + SampleKind.fromInt(kind), + timestamp, + QoS(express, congestionControl, priority), + attachmentBytes + ) + reply = Reply.Success(replierId?.let { ZenohID(it) }, sample) + } + + SampleKind.DELETE -> { + reply = Reply.Delete( + replierId?.let { ZenohID(it) }, + KeyExpr(keyExpr!!, null), + timestamp, + attachmentBytes, + QoS(express, congestionControl, priority) + ) + } } + } else { + reply = Reply.Error(replierId?.let { ZenohID(it) }, Value(payload, Encoding(ID.fromId(encodingId)!!, encodingSchema))) } - - if (value == null) { - getViaJNI( - selector.keyExpr.jniKeyExpr!!.ptr, - selector.parameters, - sessionPtr.get(), - getCallback, - onClose, - timeout.toMillis(), - target.ordinal, - consolidation.ordinal, - attachment?.let { encodeAttachment(it) } - ) - } else { - getWithValueViaJNI( - selector.keyExpr.jniKeyExpr!!.ptr, - selector.parameters, - sessionPtr.get(), - getCallback, - onClose, - timeout.toMillis(), - target.ordinal, - consolidation.ordinal, - value.payload, - value.encoding.knownEncoding.ordinal, - attachment?.let { encodeAttachment(it) } - ) + callback.run(reply) } + + getViaJNI( + selector.keyExpr.jniKeyExpr?.ptr ?: 0, + selector.keyExpr.keyExpr, + selector.parameters, + sessionPtr.get(), + getCallback, + onClose, + timeout.toMillis(), + target.ordinal, + consolidation.ordinal, + attachment, + value != null, + value?.payload, + value?.encoding?.id?.ordinal ?: 0, + value?.encoding?.schema + ) return receiver } @Throws(ZenohException::class) fun declareKeyExpr(keyExpr: String): KeyExpr { val ptr = declareKeyExprViaJNI(sessionPtr.get(), keyExpr) - return KeyExpr(JNIKeyExpr(ptr)) + return KeyExpr(keyExpr, JNIKeyExpr(ptr)) } @Throws(ZenohException::class) fun undeclareKeyExpr(keyExpr: KeyExpr) { - undeclareKeyExprViaJNI(sessionPtr.get(), keyExpr.jniKeyExpr!!.ptr) + keyExpr.jniKeyExpr?.run { + undeclareKeyExprViaJNI(sessionPtr.get(), this.ptr) + keyExpr.jniKeyExpr = null + } ?: throw SessionException("Attempting to undeclare a non declared key expression.") } - @Throws(ZenohException::class) + @Throws(Exception::class) fun performPut( keyExpr: KeyExpr, put: Put, ) { putViaJNI( - keyExpr.jniKeyExpr!!.ptr, + keyExpr.jniKeyExpr?.ptr ?: 0, + keyExpr.keyExpr, sessionPtr.get(), put.value.payload, - put.value.encoding.knownEncoding.ordinal, - put.congestionControl.ordinal, - put.priority.value, - put.kind.ordinal, - put.attachment?.let { encodeAttachment(it) } + put.value.encoding.id.ordinal, + put.value.encoding.schema, + put.qos.congestionControl.value, + put.qos.priority.value, + put.qos.express, + put.attachment ) } - @Throws(ZenohException::class) - private external fun openSessionViaJNI(configFilePath: String): Long + @Throws(Exception::class) + fun performDelete( + keyExpr: KeyExpr, + delete: Delete, + ) { + deleteViaJNI( + keyExpr.jniKeyExpr?.ptr ?: 0, + keyExpr.keyExpr, + sessionPtr.get(), + delete.qos.congestionControl.value, + delete.qos.priority.value, + delete.qos.express, + delete.attachment + ) + } + + @Throws(Exception::class) + private external fun openSessionViaJNI(configFilePath: String?): Long @Throws(Exception::class) - private external fun openSessionWithJsonConfigViaJNI(jsonConfig: String): Long + private external fun openSessionWithJsonConfigViaJNI(jsonConfig: String?): Long @Throws(Exception::class) private external fun closeSessionViaJNI(ptr: Long) - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun declarePublisherViaJNI( - keyExpr: Long, ptr: Long, congestionControl: Int, priority: Int + keyExprPtr: Long, + keyExprString: String, + sessionPtr: Long, + congestionControl: Int, + priority: Int, + express: Boolean ): Long - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun declareSubscriberViaJNI( - keyExpr: Long, + keyExprPtr: Long, + keyExprString: String, sessionPtr: Long, callback: JNISubscriberCallback, onClose: JNIOnCloseCallback, reliability: Int ): Long - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun declareQueryableViaJNI( - keyExpr: Long, + keyExprPtr: Long, + keyExprString: String, sessionPtr: Long, callback: JNIQueryableCallback, onClose: JNIOnCloseCallback, complete: Boolean ): Long - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun declareKeyExprViaJNI(sessionPtr: Long, keyExpr: String): Long - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun undeclareKeyExprViaJNI(sessionPtr: Long, keyExprPtr: Long) - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun getViaJNI( - keyExpr: Long, - selectorParams: String, - sessionPtr: Long, - callback: JNIGetCallback, - onClose: JNIOnCloseCallback, - timeoutMs: Long, - target: Int, - consolidation: Int, - attachmentBytes: ByteArray?, - ) - - @Throws(ZenohException::class) - private external fun getWithValueViaJNI( - keyExpr: Long, + keyExprPtr: Long, + keyExprString: String, selectorParams: String, sessionPtr: Long, callback: JNIGetCallback, @@ -269,20 +305,35 @@ internal class JNISession { timeoutMs: Long, target: Int, consolidation: Int, - payload: ByteArray, - encoding: Int, attachmentBytes: ByteArray?, + withValue: Boolean, + payload: ByteArray?, + encodingId: Int, + encodingSchema: String?, ) - @Throws(ZenohException::class) + @Throws(Exception::class) private external fun putViaJNI( - keyExpr: Long, + keyExprPtr: Long, + keyExprString: String, sessionPtr: Long, valuePayload: ByteArray, valueEncoding: Int, + valueEncodingSchema: String?, congestionControl: Int, priority: Int, - kind: Int, - attachmentBytes: ByteArray?, + express: Boolean, + attachmentBytes: ByteArray? + ) + + @Throws(Exception::class) + private external fun deleteViaJNI( + keyExprPtr: Long, + keyExprString: String, + sessionPtr: Long, + congestionControl: Int, + priority: Int, + express: Boolean, + attachmentBytes: ByteArray? ) } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIUtils.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIUtils.kt deleted file mode 100644 index 9c9bb5a..0000000 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIUtils.kt +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.jni - -import io.zenoh.sample.Attachment - -/** - * Encode attachment as a byte array. - */ -internal fun encodeAttachment(attachment: Attachment): ByteArray { - return attachment.values.map { - val key = it.first - val keyLength = key.size.toByteArray() - val value = it.second - val valueLength = value.size.toByteArray() - keyLength + key + valueLength + value - }.reduce { acc, bytes -> acc + bytes } -} - -/** - * Decode an attachment as a byte array, recreating the original [Attachment]. - */ -internal fun decodeAttachment(attachmentBytes: ByteArray): Attachment { - var idx = 0 - var sliceSize: Int - val pairs: MutableList> = mutableListOf() - while (idx < attachmentBytes.size) { - sliceSize = attachmentBytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1)).toInt() - idx += Int.SIZE_BYTES - - val key = attachmentBytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) - idx += sliceSize - - sliceSize = attachmentBytes.sliceArray(IntRange(idx, idx + Int.SIZE_BYTES - 1)).toInt() - idx += Int.SIZE_BYTES - - val value = attachmentBytes.sliceArray(IntRange(idx, idx + sliceSize - 1)) - idx += sliceSize - - pairs.add(key to value) - } - return Attachment(pairs) -} - -/** - * Converts an integer into a byte array with little endian format. - */ -fun Int.toByteArray(): ByteArray { - val result = ByteArray(UInt.SIZE_BYTES) - (0 until UInt.SIZE_BYTES).forEach { - result[it] = this.shr(Byte.SIZE_BITS * it).toByte() - } - return result -} - -/** - * To int. The byte array is expected to be in Little Endian format. - * - * @return The integer value. - */ -fun ByteArray.toInt(): Int = - (((this[3].toUInt() and 0xFFu) shl 24) or ((this[2].toUInt() and 0xFFu) shl 16) or ((this[1].toUInt() and 0xFFu) shl 8) or (this[0].toUInt() and 0xFFu)).toInt() diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt index 621eaf9..b78fc33 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt @@ -17,14 +17,18 @@ package io.zenoh.jni.callbacks internal fun interface JNIGetCallback { fun run( - replierId: String, + replierId: String?, success: Boolean, - keyExpr: Long, + keyExpr: String?, payload: ByteArray, - encoding: Int, + encodingId: Int, + encodingSchema: String?, kind: Int, timestampNTP64: Long, timestampIsValid: Boolean, - attachment: ByteArray, + attachment: ByteArray?, + express: Boolean, + priority: Int, + congestionControl: Int, ) } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt index 813965a..477d0dd 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt @@ -15,11 +15,14 @@ package io.zenoh.jni.callbacks internal fun interface JNIQueryableCallback { - fun run(keyExprPtr: Long, - selectorParams: String, - withValue: Boolean, - payload: ByteArray?, - encoding: Int, - attachmentBytes: ByteArray, - queryPtr: Long) + fun run( + keyExpr: String, + selectorParams: String, + withValue: Boolean, + payload: ByteArray?, + encodingId: Int, + encodingSchema: String?, + attachmentBytes: ByteArray?, + queryPtr: Long + ) } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt index 750888e..76373c7 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt @@ -16,12 +16,16 @@ package io.zenoh.jni.callbacks internal fun interface JNISubscriberCallback { fun run( - keyExpr: Long, + keyExpr: String, payload: ByteArray, - encoding: Int, + encodingId: Int, + encodingSchema: String?, kind: Int, timestampNTP64: Long, timestampIsValid: Boolean, - attachment: ByteArray, + attachment: ByteArray?, + express: Boolean, + priority: Int, + congestionControl: Int, ) } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt index e6494e1..ec13838 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -18,7 +18,6 @@ import io.zenoh.Resolvable import io.zenoh.Session import io.zenoh.exceptions.KeyExprException import io.zenoh.jni.JNIKeyExpr -import kotlin.jvm.Throws /** * # Address space @@ -46,21 +45,22 @@ import kotlin.jvm.Throws * * A KeyExpr is a string that has been validated to be a valid Key Expression. * - * # Memory + * # Declaring a key expression from a session. * - * Valid KeyExpr instances have associated an underlying native key expression, therefore we must be careful to properly - * call [close] before the KeyExpr loses any of its references and becomes a phantom reference. As a precautionary measure, - * this class overrides the [finalize] method which invokes [close] when the garbage collector attempts to remove the - * instance. However, we should not fully rely on the [finalize] method, as per to the JVM specification we don't know - * when the GC is going to be triggered, and even worse there is no guarantee it will be called at all. - * Alternatively, we can use the key expression using a try with resources statement (`use` in Kotlin), since it will - * automatically invoke the [close] function after using it. + * A [KeyExpr] acts as a container for the string representation of a key expression. Operations like `intersects`, + * `includes`, and `equals` are processed at the native layer using this string representation. For improved performance, + * consider initializing a [KeyExpr] through [Session.declareKeyExpr]. This method associates the [KeyExpr] with a native + * instance, thereby optimizing operation execution. However, it is crucial to manually invoke [close] on each [KeyExpr] + * instance before it is garbage collected to prevent memory leaks. * - * @param jniKeyExpr A [JNIKeyExpr] instance which delegates all the operations associated to this [KeyExpr] (intersects, - * includes, etc.) which are done natively. It keeps track of the underlying key expression instance. Once it is freed, - * the [KeyExpr] instance is considered to not be valid anymore. + * As an alternative, employing a try-with-resources pattern using Kotlin's `use` block is recommended. This approach + * ensures that [close] is automatically called, safely managing the lifecycle of the [KeyExpr] instance. + * + * @param keyExpr The string representation of the key expression. + * @param jniKeyExpr An optional [JNIKeyExpr] instance, present when the key expression was declared through [Session.declareKeyExpr], + * it represents the native instance of the key expression. */ -class KeyExpr internal constructor(internal var jniKeyExpr: JNIKeyExpr? = null): AutoCloseable { +class KeyExpr internal constructor(internal val keyExpr: String, internal var jniKeyExpr: JNIKeyExpr? = null): AutoCloseable { companion object { @@ -105,7 +105,7 @@ class KeyExpr internal constructor(internal var jniKeyExpr: JNIKeyExpr? = null): * Will return false as well if the key expression is not valid anymore. */ fun intersects(other: KeyExpr): Boolean { - return jniKeyExpr?.intersects(other) ?: false + return JNIKeyExpr.intersects(this, other) } /** @@ -114,7 +114,7 @@ class KeyExpr internal constructor(internal var jniKeyExpr: JNIKeyExpr? = null): * Will return false as well if the key expression is not valid anymore. */ fun includes(other: KeyExpr): Boolean { - return jniKeyExpr?.includes(other) ?: false + return JNIKeyExpr.includes(this, other) } /** @@ -135,7 +135,7 @@ class KeyExpr internal constructor(internal var jniKeyExpr: JNIKeyExpr? = null): } override fun toString(): String { - return this.jniKeyExpr?.toString() ?: "" + return keyExpr } /** @@ -146,22 +146,16 @@ class KeyExpr internal constructor(internal var jniKeyExpr: JNIKeyExpr? = null): jniKeyExpr = null } - @Suppress("removal") - protected fun finalize() { - jniKeyExpr?.close() - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as KeyExpr - if (jniKeyExpr == null || other.jniKeyExpr == null) return false - return jniKeyExpr == other.jniKeyExpr + return keyExpr == other.keyExpr } override fun hashCode(): Int { - return jniKeyExpr?.hashCode() ?: 0 + return keyExpr.hashCode() } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/CongestionControl.kt similarity index 77% rename from zenoh-java/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt rename to zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/CongestionControl.kt index 3662e54..64d981c 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/CongestionControl.kt @@ -12,19 +12,25 @@ // ZettaScale Zenoh Team, // -package io.zenoh.publication +package io.zenoh.prelude /** The congestion control to be applied when routing the data. */ -enum class CongestionControl { +enum class CongestionControl (val value: Int) { + + /** + * Allows the message to be dropped if all buffers are full. + */ + DROP(0), /** * Prevents the message from being dropped at all cost. * In the face of heavy congestion on a part of the network, this could result in your publisher node blocking. */ - BLOCK, + BLOCK(1); - /** - * Allows the message to be dropped if all buffers are full. - */ - DROP; + companion object { + fun fromInt(value: Int) = entries.first { it.value == value } + + fun default() = DROP + } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt index b466a72..22e3fdc 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt @@ -15,18 +15,18 @@ package io.zenoh.prelude /** - * The encoding of a [io.zenoh.value.Value]. + * Default encoding values used by Zenoh. * - * A zenoh encoding is an HTTP Mime type and a string suffix. + * An encoding has a similar role to Content-type in HTTP: it indicates, when present, how data should be interpreted by the application. * - * **Suffixes are not yet supported by zenoh-jni and are currently ignored.** + * Please note the Zenoh protocol does not impose any encoding value, nor it operates on it. + * It can be seen as some optional metadata that is carried over by Zenoh in such a way the application may perform different operations depending on the encoding value. + * + * A set of associated constants are provided to cover the most common encodings for user convenience. + * This is particularly useful in helping Zenoh to perform additional network optimizations. * - * @property knownEncoding A [KnownEncoding]. - * @property suffix Suffix of the encoding. This parameter is not yet supported by Zenoh-JNI and is currently ignored. */ -class Encoding(val knownEncoding: KnownEncoding, val suffix: String = "") { - - constructor(knownEncoding: KnownEncoding) : this(knownEncoding, "") +class Encoding(val id: ID, val schema: String? = null) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -34,46 +34,86 @@ class Encoding(val knownEncoding: KnownEncoding, val suffix: String = "") { other as Encoding - if (knownEncoding != other.knownEncoding) return false - return suffix == other.suffix + if (id != other.id) return false + return schema == other.schema } override fun hashCode(): Int { - var result = knownEncoding.hashCode() - result = 31 * result + suffix.hashCode() + var result = id.hashCode() + result = 31 * result + schema.hashCode() return result } -} - -/** - * Known encoding. An HTTP Mime type. - */ -enum class KnownEncoding { - EMPTY, - APP_OCTET_STREAM, - APP_CUSTOM, - TEXT_PLAIN, - APP_PROPERTIES, - APP_JSON, - APP_SQL, - APP_INTEGER, - APP_FLOAT, - APP_XML, - APP_XHTML_XML, - APP_X_WWW_FORM_URLENCODED, - TEXT_JSON, - TEXT_HTML, - TEXT_XML, - TEXT_CSS, - TEXT_CSV, - TEXT_JAVASCRIPT, - IMAGE_JPEG, - IMAGE_PNG, - IMAGE_GIF; - companion object { - fun fromInt(value: Int) = entries.first { it.ordinal == value } + /** + * The ID of the encoding. + * + * @property id The id of the encoding. + * @property encoding The encoding name. + */ + enum class ID(val id: Int, val encoding: String) { + ZENOH_BYTES(0, "zenoh/bytes"), + ZENOH_INT(1, "zenoh/int"), + ZENOH_UINT(2, "zenoh/uint"), + ZENOH_FLOAT(3, "zenoh/float"), + ZENOH_BOOL(4, "zenoh/bool"), + ZENOH_STRING(5, "zenoh/string"), + ZENOH_ERROR(6, "zenoh/error"), + APPLICATION_OCTET_STREAM(7, "application/octet-stream"), + TEXT_PLAIN(8, "text/plain"), + APPLICATION_JSON(9, "application/json"), + TEXT_JSON(10, "text/json"), + APPLICATION_CDR(11, "application/cdr"), + APPLICATION_CBOR(12, "application/cbor"), + APPLICATION_YAML(13, "application/yaml"), + TEXT_YAML(14, "text/yaml"), + TEXT_JSON5(15, "text/json5"), + APPLICATION_PYTHON_SERIALIZED_OBJECT(16, "application/python-serialized-object"), + APPLICATION_PROTOBUF(17, "application/protobuf"), + APPLICATION_JAVA_SERIALIZED_OBJECT(18, "application/java-serialized-object"), + APPLICATION_OPENMETRICS_TEXT(19, "application/openmetrics-text"), + IMAGE_PNG(20, "image/png"), + IMAGE_JPEG(21, "image/jpeg"), + IMAGE_GIF(22, "image/gif"), + IMAGE_BMP(23, "image/bmp"), + IMAGE_WEBP(24, "image/webp"), + APPLICATION_XML(25, "application/xml"), + APPLICATION_X_WWW_FORM_URLENCODED(26, "application/x-www-form-urlencoded"), + TEXT_HTML(27, "text/html"), + TEXT_XML(28, "text/xml"), + TEXT_CSS(29, "text/css"), + TEXT_JAVASCRIPT(30, "text/javascript"), + TEXT_MARKDOWN(31, "text/markdown"), + TEXT_CSV(32, "text/csv"), + APPLICATION_SQL(33, "application/sql"), + APPLICATION_COAP_PAYLOAD(34, "application/coap-payload"), + APPLICATION_JSON_PATCH_JSON(35, "application/json-patch+json"), + APPLICATION_JSON_SEQ(36, "application/json-seq"), + APPLICATION_JSONPATH(37, "application/jsonpath"), + APPLICATION_JWT(38, "application/jwt"), + APPLICATION_MP4(39, "application/mp4"), + APPLICATION_SOAP_XML(40, "application/soap+xml"), + APPLICATION_YANG(41, "application/yang"), + AUDIO_AAC(42, "audio/aac"), + AUDIO_FLAC(43, "audio/flac"), + AUDIO_MP4(44, "audio/mp4"), + AUDIO_OGG(45, "audio/ogg"), + AUDIO_VORBIS(46, "audio/vorbis"), + VIDEO_H261(47, "video/h261"), + VIDEO_H263(48, "video/h263"), + VIDEO_H264(49, "video/h264"), + VIDEO_H265(50, "video/h265"), + VIDEO_H266(51, "video/h266"), + VIDEO_MP4(52, "video/mp4"), + VIDEO_OGG(53, "video/ogg"), + VIDEO_RAW(54, "video/raw"), + VIDEO_VP8(55, "video/vp8"), + VIDEO_VP9(56, "video/vp9"); - fun default() = EMPTY + companion object { + private val idToEnum = entries.associateBy(ID::id) + fun fromId(id: Int): ID? = idToEnum[id] + } } } + + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Priority.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Priority.kt similarity index 85% rename from zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Priority.kt rename to zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Priority.kt index a8821fc..6907565 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Priority.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Priority.kt @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // -package io.zenoh.publication +package io.zenoh.prelude /** * The Priority of Zenoh messages. @@ -30,5 +30,11 @@ enum class Priority(val value: Int) { DATA(5), DATA_LOW(6), BACKGROUND(7); + + companion object { + fun fromInt(value: Int) = entries.first { it.value == value } + + fun default() = DATA + } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/QoS.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/QoS.kt new file mode 100644 index 0000000..1984006 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/QoS.kt @@ -0,0 +1,68 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +package io.zenoh.prelude + +/** + * Quality of service settings used to send zenoh message. + * + * @property express If true, the message is not batched in order to reduce the latency. + * @property congestionControl [CongestionControl] policy used for the message. + * @property priority [Priority] policy used for the message. + */ +class QoS internal constructor( + internal val express: Boolean, + internal val congestionControl: CongestionControl, + internal val priority: Priority +) { + + internal constructor(express: Boolean, congestionControl: Int, priority: Int) : this( + express, CongestionControl.fromInt(congestionControl), Priority.fromInt(priority) + ) + + /** + * Returns priority of the message. + */ + fun priority(): Priority = priority + + /** + * Returns congestion control setting of the message. + */ + fun congestionControl(): CongestionControl = congestionControl + + /** + * Returns express flag. If it is true, the message is not batched to reduce the latency. + */ + fun isExpress(): Boolean = express + + companion object { + fun default() = QoS(false, CongestionControl.default(), Priority.default()) + } + + internal class Builder( + private var express: Boolean = false, + private var congestionControl: CongestionControl = CongestionControl.default(), + private var priority: Priority = Priority.default(), + ) { + + fun express(value: Boolean) = apply { this.express = value } + + fun priority(priority: Priority) = apply { this.priority = priority } + + fun congestionControl(congestionControl: CongestionControl) = + apply { this.congestionControl = congestionControl } + + fun build() = QoS(express, congestionControl, priority) + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/protocol/ZenohID.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/protocol/ZenohID.kt new file mode 100644 index 0000000..953fb22 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/protocol/ZenohID.kt @@ -0,0 +1,20 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +package io.zenoh.protocol + +/** + * The global unique id of a Zenoh peer. + */ +class ZenohID(val id: String) diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt index 5d6036a..22cf7f6 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt @@ -14,12 +14,14 @@ package io.zenoh.publication -import io.zenoh.prelude.SampleKind +import io.zenoh.Resolvable import io.zenoh.Session import io.zenoh.exceptions.ZenohException -import io.zenoh.value.Value import io.zenoh.keyexpr.KeyExpr -import kotlin.jvm.Throws +import io.zenoh.prelude.CongestionControl +import io.zenoh.prelude.Priority +import io.zenoh.prelude.QoS +import kotlin.Throws /** * Delete operation to perform on Zenoh on a key expression. @@ -41,12 +43,8 @@ import kotlin.jvm.Throws * specifying the sample kind to be `DELETE`. */ class Delete private constructor( - keyExpr: KeyExpr, - value: Value, - congestionControl: CongestionControl, - priority: Priority, - kind: SampleKind -) : Put(keyExpr, value, congestionControl, priority, kind, null) { + val keyExpr: KeyExpr, val qos: QoS, val attachment: ByteArray? +) { companion object { /** @@ -66,36 +64,40 @@ class Delete private constructor( * * @property session The [Session] from which the Delete will be performed * @property keyExpr The [KeyExpr] from which the Delete will be performed - * @property congestionControl The [CongestionControl] to be applied when routing the data, - * defaults to [CongestionControl.DROP] - * @property priority The [Priority] of zenoh messages, defaults to [Priority.DATA]. * @constructor Create a [Delete] builder. */ class Builder internal constructor( val session: Session, val keyExpr: KeyExpr, - private var congestionControl: CongestionControl = CongestionControl.DROP, - private var priority: Priority = Priority.DATA, - ) { + ) : Resolvable { + + private var qosBuilder: QoS.Builder = QoS.Builder() + private var attachment: ByteArray? = null /** Change the [CongestionControl] to apply when routing the data. */ fun congestionControl(congestionControl: CongestionControl) = - apply { this.congestionControl = congestionControl } + apply { this.qosBuilder.congestionControl(congestionControl) } /** Change the [Priority] of the written data. */ - fun priority(priority: Priority) = apply { this.priority = priority } + fun priority(priority: Priority) = apply { this.qosBuilder.priority(priority) } + + /** + * Sets the express flag. If true, the reply won't be batched in order to reduce the latency. + */ + fun express(isExpress: Boolean) = apply { this.qosBuilder.express(isExpress) } + + /** Set an attachment to the put operation. */ + fun withAttachment(attachment: ByteArray) = apply { this.attachment = attachment } /** * Performs a DELETE operation on the specified [keyExpr]. * - * A successful resolution only states the Delete request was properly sent through the network, it doesn't mean it + * A successful [Result] only states the Delete request was properly sent through the network, it doesn't mean it * was properly executed remotely. */ @Throws(ZenohException::class) - fun res() { - val delete = Delete( - this.keyExpr, Value.empty(), this.congestionControl, this.priority, SampleKind.DELETE - ) + override fun res() { + val delete = Delete(this.keyExpr, qosBuilder.build(), attachment) session.resolveDelete(keyExpr, delete) } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt index f8f379e..e22298b 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt @@ -19,8 +19,9 @@ import io.zenoh.exceptions.SessionException import io.zenoh.exceptions.ZenohException import io.zenoh.jni.JNIPublisher import io.zenoh.keyexpr.KeyExpr -import io.zenoh.prelude.SampleKind -import io.zenoh.sample.Attachment +import io.zenoh.prelude.CongestionControl +import io.zenoh.prelude.Priority +import io.zenoh.prelude.QoS import io.zenoh.value.Value import kotlin.Throws @@ -55,16 +56,14 @@ import kotlin.Throws * The publisher configuration parameters can be later changed using the setter functions. * * @property keyExpr The key expression the publisher will be associated to. + * @property qos [QoS] configuration of the publisher. * @property jniPublisher Delegate class handling the communication with the native code. - * @property congestionControl The congestion control policy. - * @property priority The priority policy. * @constructor Create empty Publisher with the default configuration. */ class Publisher internal constructor( val keyExpr: KeyExpr, + private var qos: QoS, private var jniPublisher: JNIPublisher?, - private var congestionControl: CongestionControl, - private var priority: Priority ) : SessionDeclaration, AutoCloseable { companion object { @@ -84,41 +83,14 @@ class Publisher internal constructor( */ fun delete() = Delete(jniPublisher) - /** Get congestion control policy. */ fun getCongestionControl(): CongestionControl { - return congestionControl - } - - /** - * Set the congestion control policy of the publisher. - * - * This function is not thread safe. - * - * @param congestionControl: The [CongestionControl] policy. - */ - @Throws(ZenohException::class) - fun setCongestionControl(congestionControl: CongestionControl) { - jniPublisher?.setCongestionControl(congestionControl) - this.congestionControl = congestionControl + return qos.congestionControl() } /** Get priority policy. */ fun getPriority(): Priority { - return priority - } - - /** - * Set the priority policy of the publisher. - * - * This function is not thread safe. - * - * @param priority: The [Priority] policy. - */ - @Throws(ZenohException::class) - fun setPriority(priority: Priority) { - jniPublisher?.setPriority(priority) - this.priority = priority + return qos.priority() } override fun isValid(): Boolean { @@ -134,6 +106,7 @@ class Publisher internal constructor( jniPublisher = null } + @Suppress("removal") protected fun finalize() { jniPublisher?.close() } @@ -141,27 +114,26 @@ class Publisher internal constructor( class Put internal constructor( private var jniPublisher: JNIPublisher?, val value: Value, - var attachment: Attachment? = null + var attachment: ByteArray? = null ) : Resolvable { - fun withAttachment(attachment: Attachment) = apply { this.attachment = attachment } + fun withAttachment(attachment: ByteArray) = apply { this.attachment = attachment } - @Throws(ZenohException::class) override fun res() { - jniPublisher?.put(value, attachment) + jniPublisher?.put(value, attachment) ?: throw(sessionException) } } class Delete internal constructor( private var jniPublisher: JNIPublisher?, - var attachment: Attachment? = null + var attachment: ByteArray? = null ) : Resolvable { - fun withAttachment(attachment: Attachment) = apply { this.attachment = attachment } + fun withAttachment(attachment: ByteArray) = apply { this.attachment = attachment } @Throws(ZenohException::class) override fun res() { - jniPublisher?.delete(attachment) + jniPublisher?.delete(attachment) ?: throw(sessionException) } } @@ -170,26 +142,28 @@ class Publisher internal constructor( * * @property session The [Session] from which the publisher is declared. * @property keyExpr The key expression the publisher will be associated to. - * @property congestionControl The congestion control policy, defaults to [CongestionControl.DROP]. - * @property priority The priority policy, defaults to [Priority.DATA] * @constructor Create empty Builder. */ class Builder internal constructor( - val session: Session, - val keyExpr: KeyExpr, - var congestionControl: CongestionControl = CongestionControl.DROP, - var priority: Priority = Priority.DATA, + internal val session: Session, + internal val keyExpr: KeyExpr, ) { + private var qosBuilder: QoS.Builder = QoS.Builder() - /** Change the `congestion_control` to apply when routing the data. */ + /** Change the [CongestionControl] to apply when routing the data. */ fun congestionControl(congestionControl: CongestionControl) = - apply { this.congestionControl = congestionControl } + apply { this.qosBuilder.congestionControl(congestionControl) } + + /** Change the [Priority] of the written data. */ + fun priority(priority: Priority) = apply { this.qosBuilder.priority(priority) } - /** Change the priority of the written data. */ - fun priority(priority: Priority) = apply { this.priority = priority } + /** + * Sets the express flag. If true, the reply won't be batched in order to reduce the latency. + */ + fun express(isExpress: Boolean) = apply { this.qosBuilder.express(isExpress) } fun res(): Publisher { - return session.run { resolvePublisher(this@Builder) } + return session.run { resolvePublisher(keyExpr, qosBuilder.build()) } } } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt index 1d27f52..94266a2 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt @@ -18,9 +18,7 @@ import io.zenoh.Resolvable import io.zenoh.Session import io.zenoh.exceptions.ZenohException import io.zenoh.keyexpr.KeyExpr -import io.zenoh.prelude.Encoding -import io.zenoh.prelude.SampleKind -import io.zenoh.sample.Attachment +import io.zenoh.prelude.* import io.zenoh.value.Value /** @@ -47,18 +45,14 @@ import io.zenoh.value.Value * * @property keyExpr The [KeyExpr] to which the put operation will be performed. * @property value The [Value] to put. - * @property congestionControl The [CongestionControl] to be applied when routing the data. - * @property priority The [Priority] of zenoh messages. - * @property kind The [SampleKind] of the sample (put or delete). - * @property attachment An optional user [Attachment]. + * @property qos The [QoS] configuration. + * @property attachment An optional user attachment. */ -open class Put protected constructor( +class Put private constructor( val keyExpr: KeyExpr, val value: Value, - val congestionControl: CongestionControl, - val priority: Priority, - val kind: SampleKind, - val attachment: Attachment? + val qos: QoS, + val attachment: ByteArray? ) { companion object { @@ -71,7 +65,7 @@ open class Put protected constructor( * @param value The [Value] to put. * @return A [Put] operation [Builder]. */ - fun newBuilder(session: Session, keyExpr: KeyExpr, value: Value): Builder { + internal fun newBuilder(session: Session, keyExpr: KeyExpr, value: Value): Builder { return Builder(session, keyExpr, value) } } @@ -82,22 +76,17 @@ open class Put protected constructor( * @property session The [Session] from which the put operation will be performed. * @property keyExpr The [KeyExpr] upon which the put operation will be performed. * @property value The [Value] to put. - * @property congestionControl The [CongestionControl] to be applied when routing the data, - * defaults to [CongestionControl.DROP] - * @property priority The [Priority] of zenoh messages, defaults to [Priority.DATA]. - * @property kind The [SampleKind] of the sample (put or delete), defaults to [SampleKind.PUT]. * @constructor Create a [Put] builder. */ class Builder internal constructor( private val session: Session, private val keyExpr: KeyExpr, private var value: Value, - private var congestionControl: CongestionControl = CongestionControl.DROP, - private var priority: Priority = Priority.DATA, - private var kind: SampleKind = SampleKind.PUT, - private var attachment: Attachment? = null ): Resolvable { + private var qosBuilder: QoS.Builder = QoS.Builder() + private var attachment: ByteArray? = null + /** Change the [Encoding] of the written data. */ fun encoding(encoding: Encoding) = apply { this.value = Value(value.payload, encoding) @@ -105,21 +94,23 @@ open class Put protected constructor( /** Change the [CongestionControl] to apply when routing the data. */ fun congestionControl(congestionControl: CongestionControl) = - apply { this.congestionControl = congestionControl } + apply { this.qosBuilder.congestionControl(congestionControl) } /** Change the [Priority] of the written data. */ - fun priority(priority: Priority) = apply { this.priority = priority } + fun priority(priority: Priority) = apply { this.qosBuilder.priority(priority) } - /** Change the [SampleKind] of the sample. If set to [SampleKind.DELETE], performs a delete operation. */ - fun kind(kind: SampleKind) = apply { this.kind = kind } + /** + * Sets the express flag. If true, the reply won't be batched in order to reduce the latency. + */ + fun express(isExpress: Boolean) = apply { this.qosBuilder.express(isExpress) } /** Set an attachment to the put operation. */ - fun withAttachment(attachment: Attachment) = apply { this.attachment = attachment } + fun withAttachment(attachment: ByteArray) = apply { this.attachment = attachment } - /** Resolves the put operation. */ + /** Resolves the put operation, returning a [Result]. */ @Throws(ZenohException::class) override fun res() { - val put = Put(keyExpr, value, congestionControl, priority, kind, attachment) + val put = Put(keyExpr, value, qosBuilder.build(), attachment) session.run { resolvePut(keyExpr, put) } } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt index e430358..a95fa0d 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt @@ -16,6 +16,9 @@ package io.zenoh.query /** The kind of consolidation. */ enum class ConsolidationMode { + /** Apply automatic consolidation based on queryable's preferences. */ + AUTO, + /** No consolidation applied: multiple samples may be received for the same key-timestamp.*/ NONE, @@ -32,4 +35,8 @@ enum class ConsolidationMode { /** Holds back samples to only send the set of samples that had the highest timestamp for their key. */ LATEST; + + companion object { + fun default() = AUTO + } } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt index 11c2fd4..fcd74e9 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt @@ -19,7 +19,6 @@ import io.zenoh.Session import io.zenoh.exceptions.ZenohException import io.zenoh.handlers.BlockingQueueHandler import io.zenoh.handlers.Handler -import io.zenoh.sample.Attachment import io.zenoh.selector.Selector import io.zenoh.value.Value import java.time.Duration @@ -82,9 +81,9 @@ class Get private constructor() { private var timeout = Duration.ofMillis(10000) private var target: QueryTarget = QueryTarget.BEST_MATCHING - private var consolidation: ConsolidationMode = ConsolidationMode.NONE + private var consolidation: ConsolidationMode = ConsolidationMode.default() private var value: Value? = null - private var attachment: Attachment? = null + private var attachment: ByteArray? = null private var onClose: (() -> Unit)? = null private constructor(other: Builder<*>, handler: Handler?) : this(other.session, other.selector) { @@ -139,8 +138,8 @@ class Get private constructor() { return this } - /** Specify an [Attachment]. */ - fun withAttachment(attachment: Attachment): Builder { + /** Specify an attachment. */ + fun withAttachment(attachment: ByteArray): Builder { this.attachment = attachment return this } diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt index e0ba2e7..01d3ed9 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt @@ -21,8 +21,11 @@ import io.zenoh.sample.Sample import io.zenoh.prelude.SampleKind import io.zenoh.value.Value import io.zenoh.keyexpr.KeyExpr +import io.zenoh.prelude.CongestionControl +import io.zenoh.prelude.Priority +import io.zenoh.prelude.QoS +import io.zenoh.protocol.ZenohID import io.zenoh.queryable.Query -import io.zenoh.sample.Attachment import org.apache.commons.net.ntp.TimeStamp /** @@ -43,24 +46,20 @@ import org.apache.commons.net.ntp.TimeStamp * session.declareQueryable(keyExpr).with { query -> * query.reply(keyExpr) * .success(Value("Hello")) - * .withTimeStamp(TimeStamp(Date.from(Instant.now()))) + * .timestamp(TimeStamp(Date.from(Instant.now()))) * .res() * }.res() * ... * ``` * - * **IMPORTANT: Error replies are not yet fully supported by Zenoh, but the code for the error replies below has been - * added for the sake of future compatibility.** - * * @property replierId: unique ID identifying the replier. */ -abstract class Reply private constructor(val replierId: String) : ZenohType { +sealed class Reply private constructor(val replierId: ZenohID?) : ZenohType { /** * Builder to construct a [Reply]. * - * This builder allows you to construct [Success] replies. **Error replies are not yet enabled since they are not yet - * supported on Zenoh.** + * This builder allows you to construct [Success] and [Error] replies. * * @property query The received [Query] to reply to. * @property keyExpr The [KeyExpr] from the queryable, which is at least an intersection of the query's key expression. @@ -84,8 +83,27 @@ abstract class Reply private constructor(val replierId: String) : ZenohType { */ fun success(message: String) = success(Value(message)) -// TODO: uncomment line below when Zenoh enables Error replies. -// fun error(value: Value) = Error.Builder(query, value) + /** + * Returns an [Error.Builder] with the provided [value]. + * + * @param value The [Value] of the error reply. + */ + fun error(value: Value) = Error.Builder(query, value) + + /** + * Returns an [Error.Builder] with a [Value] containing the provided [message]. + * + * It is equivalent to calling `error(Value(message))`. + * + * @param message A string message for the error reply. + */ + fun error(message: String) = error(Value(message)) + + /** + * Returns a [Delete.Builder]. + */ + fun delete() = Delete.Builder(query, keyExpr) + } /** @@ -97,7 +115,7 @@ abstract class Reply private constructor(val replierId: String) : ZenohType { * * @param replierId The replierId of the remotely generated reply. */ - class Success internal constructor(replierId: String, val sample: Sample) : Reply(replierId) { + class Success internal constructor(replierId: ZenohID?, val sample: Sample) : Reply(replierId) { /** * Builder for the [Success] reply. @@ -106,34 +124,49 @@ abstract class Reply private constructor(val replierId: String) : ZenohType { * @property keyExpr The [KeyExpr] of the queryable. * @property value The [Value] with the reply information. */ - class Builder internal constructor(val query: Query, val keyExpr: KeyExpr, val value: Value): Resolvable { + class Builder internal constructor(val query: Query, val keyExpr: KeyExpr, val value: Value) : + Resolvable { - private var kind = SampleKind.PUT + private val kind = SampleKind.PUT private var timeStamp: TimeStamp? = null - private var attachment: Attachment? = null + private var attachment: ByteArray? = null + private var qosBuilder = QoS.Builder() /** - * Sets the [SampleKind] of the replied [Sample]. + * Sets the [TimeStamp] of the replied [Sample]. */ - fun withKind(kind: SampleKind) = apply { this.kind = kind } + fun timestamp(timeStamp: TimeStamp) = apply { this.timeStamp = timeStamp } /** - * Sets the [TimeStamp] of the replied [Sample]. + * Appends an attachment to the reply. */ - fun withTimeStamp(timeStamp: TimeStamp) = apply { this.timeStamp = timeStamp } + fun attachment(attachment: ByteArray) = apply { this.attachment = attachment } /** - * Appends an [Attachment] to the reply. + * Sets the express flag. If true, the reply won't be batched in order to reduce the latency. */ - fun withAttachment(attachment: Attachment) = apply { this.attachment = attachment } + fun express(express: Boolean) = apply { qosBuilder.express(express) } + + /** + * Sets the [Priority] of the reply. + */ + fun priority(priority: Priority) = apply { qosBuilder.priority(priority) } + + /** + * Sets the [CongestionControl] of the reply. + * + * @param congestionControl + */ + fun congestionControl(congestionControl: CongestionControl) = + apply { qosBuilder.congestionControl(congestionControl) } /** * Constructs the reply sample with the provided parameters and triggers the reply to the query. */ @Throws(ZenohException::class) override fun res() { - val sample = Sample(keyExpr, value, kind, timeStamp, attachment) - return query.reply(Success("", sample)).res() + val sample = Sample(keyExpr, value, kind, timeStamp, qosBuilder.build(), attachment) + return query.reply(Success(null, sample)).res() } } @@ -161,7 +194,7 @@ abstract class Reply private constructor(val replierId: String) : ZenohType { * * @param replierId: unique ID identifying the replier. */ - class Error internal constructor(replierId: String, val error: Value) : Reply(replierId) { + class Error internal constructor(replierId: ZenohID?, val error: Value) : Reply(replierId) { /** * Builder for the [Error] reply. @@ -169,13 +202,13 @@ abstract class Reply private constructor(val replierId: String) : ZenohType { * @property query The [Query] to reply to. * @property value The [Value] with the reply information. */ - class Builder internal constructor(val query: Query, val value: Value): Resolvable { + class Builder internal constructor(val query: Query, val value: Value) : Resolvable { /** * Triggers the error reply. */ override fun res() { - return query.reply(Error("", value)).res() + return query.reply(Error(null, value)).res() } } @@ -194,5 +227,64 @@ abstract class Reply private constructor(val replierId: String) : ZenohType { return error.hashCode() } } -} + /** + * A Delete reply. + * + * @property keyExpr + * @constructor + * + * @param replierId + */ + class Delete internal constructor( + replierId: ZenohID?, + val keyExpr: KeyExpr, + val timestamp: TimeStamp?, + val attachment: ByteArray?, + val qos: QoS + ) : Reply(replierId) { + + class Builder internal constructor(val query: Query, val keyExpr: KeyExpr) : Resolvable { + + private val kind = SampleKind.DELETE + private var timeStamp: TimeStamp? = null + private var attachment: ByteArray? = null + private var qosBuilder = QoS.Builder() + + /** + * Sets the [TimeStamp] of the replied [Sample]. + */ + fun timestamp(timeStamp: TimeStamp) = apply { this.timeStamp = timeStamp } + + /** + * Appends an attachment to the reply. + */ + fun attachment(attachment: ByteArray) = apply { this.attachment = attachment } + + /** + * Sets the express flag. If true, the reply won't be batched in order to reduce the latency. + */ + fun express(express: Boolean) = apply { qosBuilder.express(express) } + + /** + * Sets the [Priority] of the reply. + */ + fun priority(priority: Priority) = apply { qosBuilder.priority(priority) } + + /** + * Sets the [CongestionControl] of the reply. + * + * @param congestionControl + */ + fun congestionControl(congestionControl: CongestionControl) = + apply { qosBuilder.congestionControl(congestionControl) } + + /** + * Triggers the delete reply. + */ + override fun res() { + return query.reply(Delete(null, keyExpr, timeStamp, attachment, qosBuilder.build())).res() + } + } + } +} \ No newline at end of file diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt index 30d6d4a..cd4b7c8 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt @@ -19,11 +19,9 @@ import io.zenoh.ZenohType import io.zenoh.selector.Selector import io.zenoh.value.Value import io.zenoh.exceptions.SessionException -import io.zenoh.exceptions.ZenohException import io.zenoh.jni.JNIQuery import io.zenoh.keyexpr.KeyExpr import io.zenoh.query.Reply -import io.zenoh.sample.Attachment /** * Represents a Zenoh Query in Kotlin. @@ -33,7 +31,7 @@ import io.zenoh.sample.Attachment * @property keyExpr The key expression to which the query is associated. * @property selector The selector * @property value Optional value in case the received query was declared using "with query". - * @property attachment Optional [Attachment]. + * @property attachment Optional attachment. * @property jniQuery Delegate object in charge of communicating with the underlying native code. * @constructor Instances of Query objects are only meant to be created through the JNI upon receiving * a query request. Therefore, the constructor is private. @@ -42,7 +40,7 @@ class Query internal constructor( val keyExpr: KeyExpr, val selector: Selector, val value: Value?, - val attachment: Attachment?, + val attachment: ByteArray?, private var jniQuery: JNIQuery? ) : AutoCloseable, ZenohType { @@ -65,6 +63,7 @@ class Query internal constructor( } } + @Suppress("removal") protected fun finalize() { close() } @@ -72,18 +71,29 @@ class Query internal constructor( /** * Perform a reply operation to the remote [Query]. * + * A query can not be replied more than once. After the reply is performed, the query is considered + * to be no more valid and further attempts to reply to it will fail. + * * @param reply The [Reply] to the Query. - * @return A [Resolvable] that either performs the reply operation or throws an [Exception] if the query is invalid. + * @return A [Resolvable] that returns a [Result] with the status of the reply operation. */ - @Throws(ZenohException::class) internal fun reply(reply: Reply): Resolvable = Resolvable { jniQuery?.apply { - reply as Reply.Success // Since error replies are not yet supported, we assume a reply is a Success reply. - val result = replySuccess(reply.sample) - this.close() + val result = when (reply) { + is Reply.Success -> { + replySuccess(reply.sample) + } + is Reply.Error -> { + replyError(reply.error) + } + is Reply.Delete -> { + replyDelete(reply.keyExpr, reply.timestamp, reply.attachment, reply.qos) + } + } jniQuery = null return@Resolvable result } throw(SessionException("Query is invalid")) } } + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Attachment.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Attachment.kt deleted file mode 100644 index 1da85f8..0000000 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Attachment.kt +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh.sample - -/** - * Attachment - * - * An attachment consists of a list of non-unique ordered key value pairs, where keys are UTF-8 Strings and the values are bytes. - * Inserting at the same key multiple times leads to both values being transmitted for that key. - * - * Attachments can be added to a message sent through Zenoh while performing puts, queries and replies. - * - * Using attachments will result in performance loss. - * - * @property values - * @constructor Create empty Attachment - */ -class Attachment internal constructor(val values: List>) { - - class Builder { - - private val values: MutableList> = mutableListOf() - - fun add(key: ByteArray, value: ByteArray) = apply { - values.add(key to value) - } - - fun add(key: String, value: ByteArray) = apply { - values.add(key.toByteArray() to value) - } - - fun add(key: String, value: String) = apply { - values.add(key.toByteArray() to value.toByteArray()) - } - - fun addAll(elements: Collection>) { - values.addAll(elements) - } - - fun addAll(elements: Iterable>) { - values.addAll(elements) - } - - fun res(): Attachment { - return Attachment(values) - } - } -} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt index 5762d4c..e942067 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt @@ -16,6 +16,7 @@ package io.zenoh.sample import io.zenoh.ZenohType import io.zenoh.prelude.SampleKind +import io.zenoh.prelude.QoS import io.zenoh.keyexpr.KeyExpr import io.zenoh.value.Value import org.apache.commons.net.ntp.TimeStamp @@ -30,14 +31,16 @@ import org.apache.commons.net.ntp.TimeStamp * @property value The [Value] of the sample. * @property kind The [SampleKind] of the sample. * @property timestamp Optional [TimeStamp]. - * @property attachment Optional [Attachment]. + * @property qos The Quality of Service settings used to deliver the sample. + * @property attachment Optional attachment. */ class Sample( val keyExpr: KeyExpr, val value: Value, val kind: SampleKind, val timestamp: TimeStamp?, - val attachment: Attachment? = null + val qos: QoS, + val attachment: ByteArray? = null ): ZenohType { override fun toString(): String { return if (kind == SampleKind.DELETE) "$kind($keyExpr)" else "$kind($keyExpr: $value)" diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt index bc47f36..d675758 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt @@ -32,7 +32,7 @@ enum class Reliability { * * Informs the network that this subscriber wishes for all publications to reliably reach it. * - * Note that if a publisher puts a sample with the [io.zenoh.publication.CongestionControl.DROP] option, + * Note that if a publisher puts a sample with the [io.zenoh.prelude.CongestionControl.DROP] option, * this reliability requirement may be infringed to prevent slow readers from blocking the network. */ RELIABLE, diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt index d88c127..ba787bc 100644 --- a/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt @@ -15,7 +15,6 @@ package io.zenoh.value import io.zenoh.prelude.Encoding -import io.zenoh.prelude.KnownEncoding /** * A Zenoh value. @@ -28,9 +27,9 @@ import io.zenoh.prelude.KnownEncoding class Value(val payload: ByteArray, val encoding: Encoding) { /** - * Constructs a value with the provided message, using [KnownEncoding.TEXT_PLAIN] for encoding. + * Constructs a value with the provided message, using [Encoding.ID.TEXT_PLAIN] for encoding. */ - constructor(message: String): this(message.toByteArray(), Encoding(KnownEncoding.TEXT_PLAIN)) + constructor(message: String): this(message.toByteArray(), Encoding(Encoding.ID.TEXT_PLAIN)) /** * Constructs a value with the provided message and encoding. @@ -41,7 +40,7 @@ class Value(val payload: ByteArray, val encoding: Encoding) { /** Return an empty value. */ fun empty(): Value { - return Value(ByteArray(0), Encoding(KnownEncoding.APP_OCTET_STREAM)) + return Value(ByteArray(0), Encoding(Encoding.ID.ZENOH_BYTES)) } } diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/EncodingTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/EncodingTest.kt new file mode 100644 index 0000000..5f344e0 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/EncodingTest.kt @@ -0,0 +1,168 @@ +package io.zenoh + +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.Encoding +import io.zenoh.query.Reply +import io.zenoh.sample.Sample +import io.zenoh.value.Value +import kotlin.test.* + +class EncodingTest { + + @Test + fun encoding_subscriberTest() { + val session = Session.open() + val keyExpr = "example/testing/keyexpr".intoKeyExpr() + + // Testing non null schema + var receivedSample: Sample? = null + val subscriber = session.declareSubscriber(keyExpr).with { sample -> + receivedSample = sample + }.res() + var value = Value("test", Encoding(Encoding.ID.TEXT_CSV, "test_schema")) + session.put(keyExpr, value).res() + Thread.sleep(200) + + assertNotNull(receivedSample) + assertEquals(Encoding.ID.TEXT_CSV, receivedSample!!.value.encoding.id) + assertEquals("test_schema", receivedSample!!.value.encoding.schema) + + // Testing null schema + receivedSample = null + value = Value("test2", Encoding(Encoding.ID.ZENOH_STRING, null)) + session.put(keyExpr, value).res() + Thread.sleep(200) + + assertNotNull(receivedSample) + assertEquals(Encoding.ID.ZENOH_STRING, receivedSample!!.value.encoding.id) + assertNull(receivedSample!!.value.encoding.schema) + + subscriber.close() + session.close() + } + + @Test + fun encoding_replySuccessTest() { + val session = Session.open() + val keyExpr = "example/testing/**".intoKeyExpr() + val test1 = "example/testing/reply_success".intoKeyExpr() + val test2 = "example/testing/reply_success_with_schema".intoKeyExpr() + + val testValueA = Value("test", Encoding(Encoding.ID.TEXT_CSV, null)) + val testValueB = Value("test", Encoding(Encoding.ID.TEXT_CSV, "test_schema")) + + val queryable = session.declareQueryable(keyExpr).with { query -> + when (query.keyExpr) { + test1 -> query.reply(query.keyExpr).success(testValueA).res() + test2 -> query.reply(query.keyExpr).success(testValueB).res() + } + }.res() + + // Testing with null schema on a reply success scenario. + var receivedSample: Sample? = null + session.get(test1).with { reply -> + assertTrue(reply is Reply.Success) + receivedSample = reply.sample + }.res() + Thread.sleep(200) + + assertNotNull(receivedSample) + assertEquals(Encoding.ID.TEXT_CSV, receivedSample!!.value.encoding.id) + assertNull(receivedSample!!.value.encoding.schema) + + // Testing with non-null schema on a reply success scenario. + receivedSample = null + session.get(test2).with { reply -> + assertTrue(reply is Reply.Success) + receivedSample = reply.sample + }.res() + Thread.sleep(200) + + assertNotNull(receivedSample) + assertEquals(Encoding.ID.TEXT_CSV, receivedSample!!.value.encoding.id) + assertEquals("test_schema", receivedSample!!.value.encoding.schema) + + queryable.close() + session.close() + } + + @Test + fun encoding_replyErrorTest() { + val session = Session.open() + val keyExpr = "example/testing/**".intoKeyExpr() + + val test1 = "example/testing/reply_error".intoKeyExpr() + val test2 = "example/testing/reply_error_with_schema".intoKeyExpr() + + val testValueA = Value("test", Encoding(Encoding.ID.TEXT_CSV, null)) + val testValueB = Value("test", Encoding(Encoding.ID.TEXT_CSV, "test_schema")) + + val queryable = session.declareQueryable(keyExpr).with { query -> + when (query.keyExpr) { + test1 -> query.reply(query.keyExpr).error(testValueA).res() + test2 -> query.reply(query.keyExpr).error(testValueB).res() + } + }.res() + + // Testing with null schema on a reply error scenario. + var errorValue: Value? = null + session.get(test1).with { reply -> + assertTrue(reply is Reply.Error) + errorValue = reply.error + }.res() + Thread.sleep(200) + + assertNotNull(errorValue) + assertEquals(Encoding.ID.TEXT_CSV, errorValue!!.encoding.id) + assertNull(errorValue!!.encoding.schema) + + // Testing with non-null schema on a reply error scenario. + errorValue = null + session.get(test2).with { reply -> + assertTrue(reply is Reply.Error) + errorValue = reply.error + }.res() + Thread.sleep(200) + + assertNotNull(errorValue) + assertEquals(Encoding.ID.TEXT_CSV, errorValue!!.encoding.id) + assertEquals("test_schema", errorValue!!.encoding.schema) + + queryable.close() + session.close() + } + + @Test + fun encoding_queryTest() { + val session = Session.open() + val keyExpr = "example/testing/keyexpr".intoKeyExpr() + val testValueA = Value("test", Encoding(Encoding.ID.TEXT_CSV, null)) + val testValueB = Value("test", Encoding(Encoding.ID.TEXT_CSV, "test_schema")) + + var receivedValue: Value? = null + val queryable = session.declareQueryable(keyExpr).with { query -> + receivedValue = query.value + query.close() + }.res() + + // Testing with null schema + session.get(keyExpr).withValue(testValueA).res() + Thread.sleep(200) + + assertNotNull(receivedValue) + assertEquals(Encoding.ID.TEXT_CSV, receivedValue!!.encoding.id) + assertNull(receivedValue!!.encoding.schema) + + // Testing non-null schema + receivedValue = null + session.get(keyExpr).withValue(testValueB).res() + Thread.sleep(200) + + assertNotNull(receivedValue) + assertEquals(Encoding.ID.TEXT_CSV, receivedValue!!.encoding.id) + assertEquals("test_schema", receivedValue!!.encoding.schema) + + queryable.close() + session.close() + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt index 5774755..89d645b 100644 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt @@ -46,8 +46,7 @@ class GetTest { queryable = session.declareQueryable(keyExpr).with { query -> query.reply(query.keyExpr) .success(value) - .withTimeStamp(timestamp) - .withKind(kind) + .timestamp(timestamp) .res() }.res() } diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt index cbefc36..bb0b0ec 100644 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt @@ -49,11 +49,12 @@ class KeyExprTest { val keyExpr3 = KeyExpr.tryFrom("different/key/expr") assertNotEquals(keyExpr1, keyExpr3) + // Despite being undeclared, the equals operation should still work. keyExpr2.close() - assertNotEquals(keyExpr1, keyExpr2) + assertEquals(keyExpr1, keyExpr2) keyExpr1.close() - assertNotEquals(keyExpr1, keyExpr2) + assertEquals(keyExpr1, keyExpr2) } @Test @@ -63,17 +64,6 @@ class KeyExprTest { assertEquals(keyExpr1, keyExpr2) } - @Test - fun toStringTest() { - val keyExprStr = "example/test/a/b/c" - val keyExpr = KeyExpr.tryFrom(keyExprStr) - - assertEquals(keyExprStr, keyExpr.toString()) - - keyExpr.close() - assertTrue(keyExpr.toString().isEmpty()) - } - @Test fun intersectionTest() { val keyExprA = KeyExpr.tryFrom("example/*/test") diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt index b89b07c..ee5786a 100644 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt @@ -15,7 +15,6 @@ package io.zenoh import io.zenoh.keyexpr.KeyExpr -import io.zenoh.prelude.KnownEncoding import io.zenoh.keyexpr.intoKeyExpr import io.zenoh.prelude.Encoding import io.zenoh.prelude.SampleKind @@ -56,9 +55,9 @@ class PublisherTest { fun putTest() { val testValues = arrayListOf( - Value("Test 1".encodeToByteArray(), Encoding(KnownEncoding.TEXT_PLAIN)), - Value("Test 2".encodeToByteArray(), Encoding(KnownEncoding.TEXT_JSON)), - Value("Test 3".encodeToByteArray(), Encoding(KnownEncoding.TEXT_CSV)) + Value("Test 1".encodeToByteArray(), Encoding(Encoding.ID.TEXT_PLAIN)), + Value("Test 2".encodeToByteArray(), Encoding(Encoding.ID.TEXT_JSON)), + Value("Test 3".encodeToByteArray(), Encoding(Encoding.ID.TEXT_CSV)) ) testValues.forEach() { value -> publisher.put(value).res() } diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt index ffebc4b..2c34d2e 100644 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt @@ -14,7 +14,6 @@ package io.zenoh -import io.zenoh.prelude.KnownEncoding import io.zenoh.keyexpr.intoKeyExpr import io.zenoh.prelude.Encoding import io.zenoh.sample.Sample @@ -36,7 +35,7 @@ class PutTest { var receivedSample: Sample? = null val keyExpr = TEST_KEY_EXP.intoKeyExpr() val subscriber = session.declareSubscriber(keyExpr).with { sample -> receivedSample = sample }.res() - val value = Value(TEST_PAYLOAD.toByteArray(), Encoding(KnownEncoding.TEXT_PLAIN)) + val value = Value(TEST_PAYLOAD.toByteArray(), Encoding(Encoding.ID.TEXT_PLAIN)) session.put(keyExpr, value).res() subscriber.close() session.close() diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt index d21a63b..f4f724a 100644 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt @@ -17,6 +17,9 @@ package io.zenoh import io.zenoh.handlers.Handler import io.zenoh.keyexpr.KeyExpr import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.CongestionControl +import io.zenoh.prelude.Priority +import io.zenoh.prelude.QoS import io.zenoh.prelude.SampleKind import io.zenoh.query.Reply import io.zenoh.queryable.Query @@ -33,7 +36,7 @@ class QueryableTest { companion object { val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() - const val TEST_PAYLOAD = "Hello queryable" + const val testPayload = "Hello queryable" } private lateinit var session: Session @@ -55,10 +58,10 @@ class QueryableTest { @Test fun queryable_runsWithCallback() { val sample = Sample( - testKeyExpr, Value(TEST_PAYLOAD), SampleKind.PUT, TimeStamp(Date.from(Instant.now())) + testKeyExpr, Value(testPayload), SampleKind.PUT, TimeStamp(Date.from(Instant.now())), QoS.default() ) val queryable = session.declareQueryable(testKeyExpr).with { query -> - query.reply(testKeyExpr).success(sample.value).withTimeStamp(sample.timestamp!!).res() + query.reply(testKeyExpr).success(sample.value).timestamp(sample.timestamp!!).res() }.res() var reply: Reply? = null @@ -123,6 +126,82 @@ class QueryableTest { assertEquals(Value("Test value"), receivedQuery!!.value) } + @Test + fun queryReplySuccessTest() { + val message = "Test message" + val timestamp = TimeStamp.getCurrentTime() + val priority = Priority.DATA_HIGH + val express = true + val congestionControl = CongestionControl.DROP + val queryable = session.declareQueryable(testKeyExpr).with { + it.use { query -> + query.reply(testKeyExpr).success(message).timestamp(timestamp).priority(priority).express(express) + .congestionControl(congestionControl).res() + } + }.res() + + var receivedReply: Reply? = null + session.get(testKeyExpr).with { receivedReply = it }.timeout(Duration.ofMillis(10)).res() + + queryable.close() + + assertTrue(receivedReply is Reply.Success) + val reply = receivedReply as Reply.Success + assertEquals(message, reply.sample.value.payload.decodeToString()) + assertEquals(timestamp, reply.sample.timestamp) + assertEquals(priority, reply.sample.qos.priority) + assertEquals(express, reply.sample.qos.express) + assertEquals(congestionControl, reply.sample.qos.congestionControl) + } + + @Test + fun queryReplyErrorTest() { + val message = "Error message" + val queryable = session.declareQueryable(testKeyExpr).with { + it.use { query -> + query.reply(testKeyExpr).error(Value(message)).res() + } + }.res() + + var receivedReply: Reply? = null + session.get(testKeyExpr).with { receivedReply = it }.timeout(Duration.ofMillis(10)).res() + + Thread.sleep(1000) + queryable.close() + + assertNotNull(receivedReply) + assertTrue(receivedReply is Reply.Error) + val reply = receivedReply as Reply.Error + assertEquals(message, reply.error.payload.decodeToString()) + } + + @Test + fun queryReplyDeleteTest() { + val timestamp = TimeStamp.getCurrentTime() + val priority = Priority.DATA_HIGH + val express = true + val congestionControl = CongestionControl.DROP + val queryable = session.declareQueryable(testKeyExpr).with { + it.use { query -> + query.reply(testKeyExpr).delete().timestamp(timestamp).priority(priority).express(express) + .congestionControl(congestionControl).res() + } + }.res() + + var receivedReply: Reply? = null + session.get(testKeyExpr).with { receivedReply = it }.timeout(Duration.ofMillis(10)).res() + + queryable.close() + + assertNotNull(receivedReply) + assertTrue(receivedReply is Reply.Delete) + val reply = receivedReply as Reply.Delete + assertEquals(timestamp, reply.timestamp) + assertEquals(priority, reply.qos.priority) + assertEquals(express, reply.qos.express) + assertEquals(congestionControl, reply.qos.congestionControl) + } + @Test fun onCloseTest() { var onCloseWasCalled = false @@ -154,9 +233,9 @@ private class QueryHandler : Handler { val payload = "Hello queryable $counter!" counter++ val sample = Sample( - query.keyExpr, Value(payload), SampleKind.PUT, TimeStamp(Date.from(Instant.now())) + query.keyExpr, Value(payload), SampleKind.PUT, TimeStamp(Date.from(Instant.now())), QoS.default() ) performedReplies.add(sample) - query.reply(query.keyExpr).success(sample.value).withTimeStamp(sample.timestamp!!).res() + query.reply(query.keyExpr).success(sample.value).timestamp(sample.timestamp!!).res() } } diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt index be25d11..051faaa 100644 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt @@ -16,9 +16,10 @@ package io.zenoh import io.zenoh.handlers.Handler import io.zenoh.keyexpr.KeyExpr -import io.zenoh.prelude.KnownEncoding import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.CongestionControl import io.zenoh.prelude.Encoding +import io.zenoh.prelude.Priority import io.zenoh.sample.Sample import io.zenoh.value.Value import java.util.* @@ -30,10 +31,13 @@ import kotlin.test.* class SubscriberTest { companion object { + val TEST_PRIORITY = Priority.DATA_HIGH; + val TEST_CONGESTION_CONTROL = CongestionControl.BLOCK; + val testValues = arrayListOf( - Value("Test 1".encodeToByteArray(), Encoding(KnownEncoding.TEXT_PLAIN)), - Value("Test 2".encodeToByteArray(), Encoding(KnownEncoding.TEXT_JSON)), - Value("Test 3".encodeToByteArray(), Encoding(KnownEncoding.TEXT_CSV)) + Value("Test 1".encodeToByteArray(), Encoding(Encoding.ID.TEXT_PLAIN)), + Value("Test 2".encodeToByteArray(), Encoding(Encoding.ID.TEXT_JSON)), + Value("Test 3".encodeToByteArray(), Encoding(Encoding.ID.TEXT_CSV)) ) } @@ -58,11 +62,18 @@ class SubscriberTest { val subscriber = session.declareSubscriber(testKeyExpr).with { sample -> receivedSamples.add(sample) }.res() - testValues.forEach { value -> session.put(testKeyExpr, value).res() } + testValues.forEach { value -> + session.put(testKeyExpr, value) + .priority(TEST_PRIORITY) + .congestionControl(TEST_CONGESTION_CONTROL) + .res() + } assertEquals(receivedSamples.size, testValues.size) receivedSamples.zip(testValues).forEach { (sample, value) -> assertEquals(sample.value, value) + assertEquals(sample.qos.priority(), TEST_PRIORITY) + assertEquals(sample.qos.congestionControl(), TEST_CONGESTION_CONTROL) } subscriber.close() @@ -73,11 +84,18 @@ class SubscriberTest { val handler = QueueHandler() val subscriber = session.declareSubscriber(testKeyExpr).with(handler).res() - testValues.forEach { value -> session.put(testKeyExpr, value).res() } + testValues.forEach { value -> + session.put(testKeyExpr, value) + .priority(TEST_PRIORITY) + .congestionControl(TEST_CONGESTION_CONTROL) + .res() + } assertEquals(handler.queue.size, testValues.size) handler.queue.zip(testValues).forEach { (sample, value) -> assertEquals(sample.value, value) + assertEquals(sample.qos.priority(), TEST_PRIORITY) + assertEquals(sample.qos.congestionControl(), TEST_CONGESTION_CONTROL) } subscriber.close() @@ -90,6 +108,24 @@ class SubscriberTest { assertTrue(subscriber.receiver is BlockingQueue>) } + @Test + fun subscriber_isDeclaredWithNonDeclaredKeyExpression() { + // Declaring a subscriber with an undeclared key expression and verifying it properly receives samples. + val keyExpr = KeyExpr("example/**") + val session = Session.open() + + val receivedSamples = ArrayList() + val subscriber = session.declareSubscriber(keyExpr).with { sample -> receivedSamples.add(sample) }.res() + testValues.forEach { value -> session.put(testKeyExpr, value).res() } + subscriber.close() + + assertEquals(receivedSamples.size, testValues.size) + + for ((index, sample) in receivedSamples.withIndex()) { + assertEquals(sample.value, testValues[index]) + } + } + @Test fun onCloseTest() { var onCloseWasCalled = false diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt deleted file mode 100644 index ca6f050..0000000 --- a/zenoh-java/src/commonTest/kotlin/io/zenoh/UserAttachmentTest.kt +++ /dev/null @@ -1,216 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -package io.zenoh - -import io.zenoh.jni.decodeAttachment -import io.zenoh.jni.encodeAttachment -import io.zenoh.jni.toByteArray -import io.zenoh.jni.toInt -import io.zenoh.keyexpr.KeyExpr -import io.zenoh.keyexpr.intoKeyExpr -import io.zenoh.prelude.Encoding -import io.zenoh.prelude.KnownEncoding -import io.zenoh.query.Reply -import io.zenoh.sample.Attachment -import io.zenoh.sample.Sample -import io.zenoh.value.Value -import java.time.Duration -import kotlin.test.* - - -class UserAttachmentTest { - - private lateinit var session: Session - private lateinit var keyExpr: KeyExpr - - companion object { - val value = Value("test", Encoding(KnownEncoding.TEXT_PLAIN)) - const val keyExprString = "example/testing/attachment" - val attachmentPairs = arrayListOf( - "key1" to "value1", "key2" to "value2", "key3" to "value3", "repeatedKey" to "value1", "repeatedKey" to "value2" - ) - val attachment = - Attachment(attachmentPairs.map { it.first.encodeToByteArray() to it.second.encodeToByteArray() }) - } - - @BeforeTest - fun setup() { - session = Session.open() - keyExpr = keyExprString.intoKeyExpr() - } - - @AfterTest - fun tearDown() { - session.close() - keyExpr.close() - } - - private fun assertAttachmentOk(attachment: Attachment?) { - assertNotNull(attachment) - val receivedPairs = attachment.values - assertEquals(attachmentPairs.size, receivedPairs.size) - for ((index, receivedPair) in receivedPairs.withIndex()) { - assertEquals(attachmentPairs[index].first, receivedPair.first.decodeToString()) - assertEquals(attachmentPairs[index].second, receivedPair.second.decodeToString()) - } - } - - @Test - fun putWithAttachmentTest() { - var receivedSample: Sample? = null - val subscriber = session.declareSubscriber(keyExpr).with { sample -> receivedSample = sample }.res() - session.put(keyExpr, value).withAttachment(attachment).res() - - subscriber.close() - - assertNotNull(receivedSample) { - assertEquals(value, it.value) - assertAttachmentOk(it.attachment) - } - } - - @Test - fun publisherPutWithAttachmentTest() { - var receivedSample: Sample? = null - val publisher = session.declarePublisher(keyExpr).res() - val subscriber = session.declareSubscriber(keyExpr).with { sample -> - receivedSample = sample - }.res() - - publisher.put("test").withAttachment(attachment).res() - - publisher.close() - subscriber.close() - - assertAttachmentOk(receivedSample!!.attachment!!) - } - - @Test - fun publisherPutWithoutAttachmentTest() { - var receivedSample: Sample? = null - val publisher = session.declarePublisher(keyExpr).res() - val subscriber = session.declareSubscriber(keyExpr).with { sample -> receivedSample = sample }.res() - - publisher.put("test").res() - - publisher.close() - subscriber.close() - - assertNotNull(receivedSample) { - assertNull(it.attachment) - } - } - - @Test - fun publisherDeleteWithAttachmentTest() { - var receivedSample: Sample? = null - val publisher = session.declarePublisher(keyExpr).res() - val subscriber = session.declareSubscriber(keyExpr).with { sample -> receivedSample = sample }.res() - - publisher.delete().withAttachment(attachment).res() - - publisher.close() - subscriber.close() - - assertAttachmentOk(receivedSample?.attachment) - } - - @Test - fun publisherDeleteWithoutAttachmentTest() { - var receivedSample: Sample? = null - val publisher = session.declarePublisher(keyExpr).res() - val subscriber = session.declareSubscriber(keyExpr).with { sample -> receivedSample = sample }.res() - - publisher.delete().res() - - publisher.close() - subscriber.close() - - assertNotNull(receivedSample) { - assertNull(it.attachment) - } - } - - @Test - fun queryWithAttachmentTest() { - var receivedAttachment: Attachment? = null - val queryable = session.declareQueryable(keyExpr).with { query -> - receivedAttachment = query.attachment - query.reply(keyExpr).success("test").res() - }.res() - - session.get(keyExpr).with {}.withAttachment(attachment).timeout(Duration.ofMillis(1000)).res() - - queryable.close() - - assertAttachmentOk(receivedAttachment) - } - - @Test - fun queryReplyWithAttachmentTest() { - var receivedAttachment: Attachment? = null - val queryable = session.declareQueryable(keyExpr).with { query -> - query.reply(keyExpr).success("test").withAttachment(attachment).res() - }.res() - - session.get(keyExpr).with { reply -> - if (reply is Reply.Success) { - receivedAttachment = reply.sample.attachment - } - }.timeout(Duration.ofMillis(1000)).res() - - queryable.close() - - assertAttachmentOk(receivedAttachment) - } - - @Test - fun queryReplyWithoutAttachmentTest() { - var reply: Reply? = null - val queryable = session.declareQueryable(keyExpr).with { query -> - query.reply(keyExpr).success("test").res() - }.res() - - session.get(keyExpr).with { - reply = it - }.timeout(Duration.ofMillis(1000)).res() - - queryable.close() - - assertNotNull(reply) { - assertTrue(it is Reply.Success) - assertNull(it.sample.attachment) - } - } - - @Test - fun encodeAndDecodeNumbersTest() { - val numbers: List = arrayListOf(0, 1, -1, 12345, -12345, 123567, 123456789, -123456789) - - numbers.forEach { number -> - val bytes = number.toByteArray() - val decodedNumber: Int = bytes.toInt() - assertEquals(number, decodedNumber) - } - } - - @Test - fun encodeAndDecodeAttachmentTest() { - val encodedAttachment = encodeAttachment(attachment) - val decodedAttachment = decodeAttachment(encodedAttachment) - - assertAttachmentOk(decodedAttachment) - } -} diff --git a/zenoh-jni/Cargo.lock b/zenoh-jni/Cargo.lock index 55e17e5..87ba187 100644 --- a/zenoh-jni/Cargo.lock +++ b/zenoh-jni/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "array-init" @@ -86,6 +86,45 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" +[[package]] +name = "asn1-rs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -97,13 +136,24 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-executor" -version = "1.5.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" dependencies = [ - "async-lock", "async-task", "concurrent-queue", "fastrand", @@ -113,11 +163,11 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-executor", "async-io", "async-lock", @@ -129,49 +179,32 @@ dependencies = [ [[package]] name = "async-io" -version = "1.13.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ "async-lock", - "autocfg", "cfg-if", "concurrent-queue", + "futures-io", "futures-lite", - "log", "parking", "polling", "rustix", "slab", - "socket2 0.4.9", - "waker-fn", + "tracing", + "windows-sys 0.52.0", ] [[package]] name = "async-lock" -version = "2.8.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-process" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if", - "event-listener 2.5.3", - "futures-lite", - "rustix", - "signal-hook", - "windows-sys 0.48.0", + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", ] [[package]] @@ -179,27 +212,6 @@ name = "async-std" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] [[package]] name = "async-task" @@ -209,13 +221,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] @@ -262,6 +274,12 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -303,17 +321,15 @@ dependencies = [ [[package]] name = "blocking" -version = "1.3.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", - "async-lock", + "async-channel 2.3.1", "async-task", - "atomic-waker", - "fastrand", + "futures-io", "futures-lite", - "log", + "piper", ] [[package]] @@ -330,9 +346,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cache-padded" @@ -361,6 +377,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cipher" version = "0.4.4" @@ -407,9 +429,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.2.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -422,18 +444,18 @@ checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "const_format" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ "proc-macro2", "quote", @@ -467,12 +489,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -501,6 +520,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.3.11" @@ -543,12 +576,29 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "dyn-clone" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "env_logger" version = "0.10.2" @@ -583,9 +633,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "4.0.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -593,14 +643,21 @@ dependencies = [ ] [[package]] -name = "fastrand" -version = "1.9.0" +name = "event-listener-strategy" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "instant", + "event-listener 5.3.1", + "pin-project-lite", ] +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -617,7 +674,7 @@ dependencies = [ "futures-sink", "nanorand", "pin-project", - "spin 0.9.8", + "spin", ] [[package]] @@ -629,7 +686,7 @@ dependencies = [ "futures-core", "futures-sink", "nanorand", - "spin 0.9.8", + "spin", ] [[package]] @@ -640,18 +697,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -664,9 +721,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -674,15 +731,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -691,15 +748,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" -version = "1.13.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +checksum = "9c1155db57329dca6d018b61e76b1488ce9a2e5e44028cac420a5898f4fcef63" dependencies = [ "fastrand", "futures-core", @@ -712,32 +769,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -782,37 +839,29 @@ checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "git-version" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899" +checksum = "1ad568aa3db0fcbc81f2f116137f263d7304f512a1209b35b85150d3ef88ad19" dependencies = [ "git-version-macro", - "proc-macro-hack", ] [[package]] name = "git-version-macro" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" +checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ - "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] -name = "gloo-timers" -version = "0.2.6" +name = "half" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" @@ -822,18 +871,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -850,9 +890,15 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hmac" @@ -865,11 +911,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -897,9 +943,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -917,12 +963,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] @@ -935,32 +981,27 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.12" +name = "ipnetwork" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" dependencies = [ - "cfg-if", + "serde", ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "iter-read" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.2", - "libc", - "windows-sys 0.48.0", -] +checksum = "c397ca3ea05ad509c4ec451fea28b4771236a376ca1c69fd5143aae0cf8f93c4" [[package]] -name = "ipnetwork" -version = "0.20.0" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "serde", + "either", ] [[package]] @@ -1036,36 +1077,27 @@ dependencies = [ [[package]] name = "keyed-set" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79e110283e09081809ca488cf3a9709270c6d4d4c4a32674c39cc438366615a" -dependencies = [ - "hashbrown 0.13.2", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +checksum = "0a3ec39d2dc17953a1540d63906a112088f79b2e46833b4ed65bc9de3904ae34" dependencies = [ - "log", + "hashbrown 0.14.5", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] name = "libc" -version = "0.2.153" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -1085,9 +1117,9 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -1104,9 +1136,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] [[package]] name = "lz4_flex" @@ -1128,9 +1157,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" @@ -1143,13 +1178,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi 0.3.9", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1163,12 +1199,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.5.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -1178,6 +1215,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1224,19 +1271,18 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -1245,9 +1291,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -1259,7 +1305,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.9", "libc", ] @@ -1272,6 +1318,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -1292,9 +1347,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-float" -version = "4.1.1" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "536900a8093134cf9ccf00a27deb3532421099e958d9dd431135d0c7543ca1e8" +checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" dependencies = [ "num-traits", ] @@ -1342,9 +1397,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem-rfc7468" @@ -1357,9 +1412,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -1392,7 +1447,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] @@ -1408,12 +1463,54 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap 2.4.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", ] [[package]] @@ -1433,7 +1530,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] @@ -1448,6 +1545,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -1471,18 +1579,18 @@ dependencies = [ [[package]] name = "pnet_base" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c" +checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7" dependencies = [ "no-std-net", ] [[package]] name = "pnet_datalink" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad5854abf0067ebbd3967f7d45ebc8976ff577ff0c7bd101c4973ae3c70f98fe" +checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7" dependencies = [ "ipnetwork", "libc", @@ -1493,9 +1601,9 @@ dependencies = [ [[package]] name = "pnet_sys" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "417c0becd1b573f6d544f73671070b039051e5ad819cc64aa96377b536128d00" +checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b" dependencies = [ "libc", "winapi", @@ -1503,18 +1611,17 @@ dependencies = [ [[package]] name = "polling" -version = "2.8.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ - "autocfg", - "bitflags 1.3.2", "cfg-if", "concurrent-queue", - "libc", - "log", + "hermit-abi 0.4.0", "pin-project-lite", - "windows-sys 0.48.0", + "rustix", + "tracing", + "windows-sys 0.52.0", ] [[package]] @@ -1529,33 +1636,28 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quinn" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904e3d3ba178131798c6d9375db2b13b34337d489b089fc5ba0825a2ff1bee73" +checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.0.0", "rustls", + "socket2", "thiserror", "tokio", "tracing", @@ -1563,14 +1665,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e974563a4b1c2206bbc61191ca4da9c22e4308b4c455e8906751cc7828393f08" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" dependencies = [ "bytes", "rand", "ring", - "rustc-hash", + "rustc-hash 1.1.0", "rustls", "rustls-platform-verifier", "slab", @@ -1581,22 +1683,22 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f0def2590301f4f667db5a77f9694fb004f82796dc1a8b1508fafa3d0e8b72" +checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" dependencies = [ "libc", "once_cell", - "socket2 0.5.6", + "socket2", "tracing", "windows-sys 0.52.0", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1671,14 +1773,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -1692,13 +1794,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.4", ] [[package]] @@ -1709,9 +1811,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ring" @@ -1722,7 +1824,7 @@ dependencies = [ "cc", "getrandom", "libc", - "spin 0.9.8", + "spin", "untrusted", "windows-sys 0.48.0", ] @@ -1743,7 +1845,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64", + "base64 0.21.4", "bitflags 2.5.0", "serde", "serde_derive", @@ -1783,34 +1885,48 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" -version = "0.37.25" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "log", "once_cell", @@ -1836,19 +1952,19 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.0.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ - "base64", + "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-platform-verifier" @@ -1879,9 +1995,9 @@ checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad" [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" dependencies = [ "ring", "rustls-pki-types", @@ -1914,11 +2030,12 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.13" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", + "either", "schemars_derive", "serde", "serde_json", @@ -1926,14 +2043,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.13" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] @@ -1984,53 +2101,77 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-pickle" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762ad136a26407c6a80825813600ceeab5e613660d93d79a41f0ec877171e71" +dependencies = [ + "byteorder", + "iter-read", + "num-bigint", + "num-traits", + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_yaml" -version = "0.9.25" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.4.0", "itoa", "ryu", "serde", @@ -2087,25 +2228,6 @@ dependencies = [ "dirs", ] -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "signature" version = "2.1.0" @@ -2116,6 +2238,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -2127,36 +2255,20 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -2188,7 +2300,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" dependencies = [ - "async-channel", + "async-channel 1.9.0", "cfg-if", "futures-core", "pin-project-lite", @@ -2219,15 +2331,26 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.33" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2260,7 +2383,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] @@ -2330,30 +2453,29 @@ dependencies = [ [[package]] name = "tokio" -version = "1.36.0" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", - "socket2 0.5.6", + "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] @@ -2369,9 +2491,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" dependencies = [ "futures-util", "log", @@ -2381,15 +2503,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "futures-util", - "hashbrown 0.14.0", + "hashbrown 0.14.5", "pin-project-lite", "tokio", ] @@ -2415,7 +2537,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] @@ -2472,9 +2594,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.21.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" dependencies = [ "byteorder", "bytes", @@ -2485,7 +2607,6 @@ dependencies = [ "rand", "sha1", "thiserror", - "url", "utf-8", ] @@ -2513,23 +2634,23 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uhlc" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b6df3f3e948b40e20c38a6d1fd6d8f91b3573922fc164e068ad3331560487e" +checksum = "79ac3c37bd9506595768f0387bd39d644525728b4a1d783218acabfb56356db7" dependencies = [ "humantime", "lazy_static", "log", "rand", "serde", - "spin 0.9.8", + "spin", ] [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2539,9 +2660,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -2554,9 +2675,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -2564,6 +2685,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unwrap-infallible" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" + [[package]] name = "unzip-n" version = "0.1.2" @@ -2577,9 +2704,9 @@ dependencies = [ [[package]] name = "url" -version = "2.4.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2594,9 +2721,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.4.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", ] @@ -2631,12 +2758,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" - [[package]] name = "vec_map" version = "0.8.2" @@ -2692,22 +2813,10 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -2726,7 +2835,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2737,21 +2846,11 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webpki-roots" -version = "0.26.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] @@ -2985,35 +3084,59 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "zenoh" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "ahash", "async-trait", - "base64", - "const_format", - "event-listener 4.0.0", + "base64 0.22.1", + "bytes", + "event-listener 5.3.1", "flume 0.11.0", "form_urlencoded", "futures", "git-version", + "itertools", "lazy_static", + "once_cell", "ordered-float", "paste", "petgraph", + "phf", "rand", "regex", "rustc_version", "serde", + "serde-pickle", + "serde_cbor", "serde_json", - "socket2 0.5.6", + "serde_yaml", + "socket2", "stop-token", "tokio", "tokio-util", "tracing", "uhlc", + "unwrap-infallible", "uuid", "vec_map", "zenoh-buffers", @@ -3037,16 +3160,16 @@ dependencies = [ [[package]] name = "zenoh-buffers" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "zenoh-collections", ] [[package]] name = "zenoh-codec" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "serde", "tracing", @@ -3057,13 +3180,13 @@ dependencies = [ [[package]] name = "zenoh-collections" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" [[package]] name = "zenoh-config" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "flume 0.11.0", "json5", @@ -3073,8 +3196,10 @@ dependencies = [ "serde_json", "serde_yaml", "tracing", + "uhlc", "validated_struct", "zenoh-core", + "zenoh-macros", "zenoh-protocol", "zenoh-result", "zenoh-util", @@ -3082,8 +3207,8 @@ dependencies = [ [[package]] name = "zenoh-core" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-global-executor", "lazy_static", @@ -3094,8 +3219,8 @@ dependencies = [ [[package]] name = "zenoh-crypto" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "aes", "hmac", @@ -3107,31 +3232,29 @@ dependencies = [ [[package]] name = "zenoh-ext" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "bincode", "flume 0.11.0", "futures", + "phf", "serde", + "serde_cbor", + "serde_json", "tokio", "tracing", "zenoh", - "zenoh-core", "zenoh-macros", - "zenoh-result", - "zenoh-runtime", - "zenoh-sync", - "zenoh-task", "zenoh-util", ] [[package]] name = "zenoh-keyexpr" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.5", "keyed-set", "rand", "schemars", @@ -3142,8 +3265,8 @@ dependencies = [ [[package]] name = "zenoh-link" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", "zenoh-config", @@ -3160,8 +3283,8 @@ dependencies = [ [[package]] name = "zenoh-link-commons" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", "flume 0.11.0", @@ -3172,6 +3295,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "webpki-roots", "zenoh-buffers", "zenoh-codec", "zenoh-config", @@ -3184,11 +3308,11 @@ dependencies = [ [[package]] name = "zenoh-link-quic" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "futures", "quinn", "rustls", @@ -3201,6 +3325,8 @@ dependencies = [ "tokio-util", "tracing", "webpki-roots", + "x509-parser", + "zenoh-collections", "zenoh-config", "zenoh-core", "zenoh-link-commons", @@ -3213,10 +3339,11 @@ dependencies = [ [[package]] name = "zenoh-link-tcp" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", + "socket2", "tokio", "tokio-util", "tracing", @@ -3231,22 +3358,25 @@ dependencies = [ [[package]] name = "zenoh-link-tls" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "futures", "rustls", "rustls-pemfile", "rustls-pki-types", "rustls-webpki", "secrecy", + "socket2", "tokio", "tokio-rustls", "tokio-util", "tracing", "webpki-roots", + "x509-parser", + "zenoh-collections", "zenoh-config", "zenoh-core", "zenoh-link-commons", @@ -3259,11 +3389,11 @@ dependencies = [ [[package]] name = "zenoh-link-udp" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", - "socket2 0.5.6", + "socket2", "tokio", "tokio-util", "tracing", @@ -3280,8 +3410,8 @@ dependencies = [ [[package]] name = "zenoh-link-unixsock_stream" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", "futures", @@ -3300,8 +3430,8 @@ dependencies = [ [[package]] name = "zenoh-link-ws" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", "futures-util", @@ -3321,21 +3451,20 @@ dependencies = [ [[package]] name = "zenoh-macros" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", "zenoh-keyexpr", ] [[package]] name = "zenoh-plugin-trait" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ - "const_format", "libloading", "serde", "serde_json", @@ -3348,30 +3477,31 @@ dependencies = [ [[package]] name = "zenoh-protocol" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "const_format", "rand", "serde", "uhlc", "zenoh-buffers", + "zenoh-collections", "zenoh-keyexpr", "zenoh-result", ] [[package]] name = "zenoh-result" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "anyhow", ] [[package]] name = "zenoh-runtime" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "futures", "lazy_static", @@ -3379,17 +3509,16 @@ dependencies = [ "ron", "serde", "tokio", - "zenoh-collections", "zenoh-macros", "zenoh-result", ] [[package]] name = "zenoh-sync" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ - "event-listener 4.0.0", + "event-listener 5.3.1", "futures", "tokio", "zenoh-buffers", @@ -3400,8 +3529,8 @@ dependencies = [ [[package]] name = "zenoh-task" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "futures", "tokio", @@ -3413,11 +3542,13 @@ dependencies = [ [[package]] name = "zenoh-transport" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ "async-trait", + "crossbeam-utils", "flume 0.11.0", + "lazy_static", "lz4_flex", "paste", "rand", @@ -3445,11 +3576,11 @@ dependencies = [ [[package]] name = "zenoh-util" -version = "0.11.0-dev" -source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#e66745ebdf70f01bf01ba60f69bc902b917b2d24" +version = "1.0.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=main#7ff82cfd816fcf998a1a5b3582b04b9984260d88" dependencies = [ - "async-std", "async-trait", + "const_format", "flume 0.11.0", "home", "humantime", @@ -3457,6 +3588,8 @@ dependencies = [ "libc", "libloading", "pnet_datalink", + "serde", + "serde_json", "shellexpand", "tokio", "tracing", @@ -3468,7 +3601,7 @@ dependencies = [ [[package]] name = "zenoh_jni" -version = "0.11.0-dev" +version = "1.0.0-dev" dependencies = [ "android-logd-logger", "async-std", @@ -3481,7 +3614,7 @@ dependencies = [ "uhlc", "zenoh", "zenoh-ext", - "zenoh-util", + "zenoh-protocol", ] [[package]] @@ -3501,7 +3634,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.33", + "syn 2.0.52", ] [[package]] diff --git a/zenoh-jni/Cargo.toml b/zenoh-jni/Cargo.toml index e3148bc..c5f62f8 100644 --- a/zenoh-jni/Cargo.toml +++ b/zenoh-jni/Cargo.toml @@ -13,8 +13,8 @@ # [package] -rust-version = "1.66.1" -version = "0.11.0-dev" # Zenoh version +rust-version = "1.75.0" +version = "1.0.0-dev" repository = "https://github.com/eclipse-zenoh/zenoh" homepage = "http://zenoh.io" edition = "2021" @@ -33,11 +33,11 @@ async-std = { version = "=1.12.0", default-features = false } clap = "3.2.23" jni = "0.21.1" flume = "0.10.14" -uhlc = "0.7.0" +uhlc = "0.8.0" json5 = "0.4.1" -zenoh = { version = "0.11.0-dev", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", default-features = false } -zenoh-ext = { version = "0.11.0-dev", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", default-features = false } -zenoh-util = { version = "0.11.0-dev", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main" } +zenoh = { version = "1.0.0-dev", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", default-features = false } +zenoh-ext = { version = "1.0.0-dev", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", default-features = false } +zenoh-protocol = { version = "1.0.0-dev", git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", default-features = false } tracing = "0.1" [lib] name = "zenoh_jni" diff --git a/zenoh-jni/src/errors.rs b/zenoh-jni/src/errors.rs index 337a3ba..edca4ab 100644 --- a/zenoh-jni/src/errors.rs +++ b/zenoh-jni/src/errors.rs @@ -16,6 +16,46 @@ use std::fmt; use jni::JNIEnv; +#[macro_export] +macro_rules! throw_exception { + ($env:expr, $err:expr) => {{ + let _ = $err.throw_on_jvm(&mut $env).map_err(|err| { + tracing::error!("Unable to throw exception: {}", err); + }); + }}; +} + +#[macro_export] +macro_rules! jni_error { + ($arg:expr) => { + Error::Jni($arg.to_string()) + }; + ($fmt:expr, $($arg:tt)*) => { + Error::Jni(format!($fmt, $($arg)*)) + }; +} + +#[macro_export] +macro_rules! session_error { + ($arg:expr) => { + $crate::errors::Error::Session($arg.to_string()) + }; + ($fmt:expr, $($arg:tt)*) => { + Error::Session(format!($fmt, $($arg)*)) + }; + +} + +#[macro_export] +macro_rules! key_expr_error { + ($arg:expr) => { + Error::KeyExpr($arg.to_string()) + }; + ($fmt:expr, $($arg:tt)*) => { + Error::KeyExpr(format!($fmt, $($arg)*)) + }; +} + pub(crate) type Result = core::result::Result; #[derive(Debug)] @@ -49,8 +89,8 @@ impl Error { let exception_name = self.get_associated_kotlin_exception(); let exception_class = env .find_class(&exception_name) - .map_err(|err| Error::Jni(format!("Failed to retrieve exception class: {}", err)))?; + .map_err(|err| jni_error!("Failed to retrieve exception class: {}", err))?; env.throw_new(exception_class, self.to_string()) - .map_err(|err| Error::Jni(format!("Failed to throw exception: {}", err))) + .map_err(|err| jni_error!("Failed to throw exception: {}", err)) } } diff --git a/zenoh-jni/src/key_expr.rs b/zenoh-jni/src/key_expr.rs index 9f8335c..b4fd0fe 100644 --- a/zenoh-jni/src/key_expr.rs +++ b/zenoh-jni/src/key_expr.rs @@ -12,143 +12,198 @@ // ZettaScale Zenoh Team, // -use std::ptr::null; +use std::ops::Deref; use std::sync::Arc; use jni::objects::JClass; use jni::sys::{jboolean, jstring}; use jni::{objects::JString, JNIEnv}; -use zenoh::prelude::KeyExpr; +use zenoh::key_expr::KeyExpr; use crate::errors::Error; +use crate::errors::Result; use crate::utils::decode_string; +use crate::{jni_error, key_expr_error, throw_exception}; +/// Validates the provided `key_expr` to be a valid key expression, returning it back +/// in case of success or throwing an exception in case of failure. +/// +/// # Parameters: +/// `env`: The JNI environment. +/// `_class`: the Java class (unused). +/// `key_expr`: Java string representation of the intended key expression. +/// #[no_mangle] #[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_tryFromViaJNI( +pub extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_tryFromViaJNI( mut env: JNIEnv, _class: JClass, key_expr: JString, -) -> *const KeyExpr<'static> { - let key_expr_str = match decode_string(&mut env, key_expr) { - Ok(key_expr) => key_expr, - Err(err) => { - _ = err.throw_on_jvm(&mut env); - return null(); - } - }; - let key_expr = match KeyExpr::try_from(key_expr_str) { - Ok(key_expr) => key_expr, - Err(err) => { - _ = Error::KeyExpr(err.to_string()).throw_on_jvm(&mut env); - return null(); - } - }; - Arc::into_raw(Arc::new(key_expr)) +) -> jstring { + decode_key_expr(&mut env, &key_expr) + .map(|_| **key_expr) + .unwrap_or_else(|err| { + throw_exception!(env, err); + JString::default().as_raw() + }) } +/// Returns a java string representation of the autocanonized version of the provided `key_expr`. +/// In case of failure and exception will be thrown. +/// +/// # Parameters: +/// `env`: The JNI environment. +/// `_class`: the Java class (unused). +/// `key_expr`: Java string representation of the intended key expression. +/// #[no_mangle] #[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_autocanonizeViaJNI( +pub extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_autocanonizeViaJNI( mut env: JNIEnv, _class: JClass, key_expr: JString, -) -> *const KeyExpr<'static> { - let key_expr_str = match decode_string(&mut env, key_expr) { - Ok(key_expr) => key_expr, - Err(err) => { - _ = err.throw_on_jvm(&mut env); - return null(); - } - }; - let key_expr = match KeyExpr::autocanonize(key_expr_str) { - Ok(key_expr) => key_expr, - Err(err) => { - _ = Error::KeyExpr(err.to_string()).throw_on_jvm(&mut env); - return null(); - } - }; - Arc::into_raw(Arc::new(key_expr)) +) -> jstring { + autocanonize_key_expr(&mut env, &key_expr) + .and_then(|key_expr| { + env.new_string(key_expr.to_string()) + .map(|kexp| kexp.as_raw()) + .map_err(|err| jni_error!(err)) + }) + .unwrap_or_else(|err| { + throw_exception!(env, err); + JString::default().as_raw() + }) } +/// Returns true in case key_expr_1 intersects key_expr_2. +/// +/// # Params: +/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_2`: String representation of the key expression 2. +/// +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing +/// key expressions that were declared from a session (in that case the key expression has a pointer associated). +/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers +/// remain valid after the call to this function. +/// #[no_mangle] #[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_getStringValueViaJNI( +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_intersectsViaJNI( mut env: JNIEnv, _: JClass, - ptr: *const KeyExpr<'static>, -) -> jstring { - let key_expr = Arc::from_raw(ptr); - let key_expr_str = match env.new_string(key_expr.to_string()) { - Ok(key_expr) => key_expr, - Err(err) => { - _ = Error::Jni(format!( - "Unable to get key expression string value: {}", - err - )) - .throw_on_jvm(&mut env); - std::mem::forget(key_expr); - return JString::default().as_raw(); - } - }; - std::mem::forget(key_expr); - key_expr_str.as_raw() + key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_1: JString, + key_expr_ptr_2: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_2: JString, +) -> jboolean { + || -> Result { + let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; + let key_expr_2 = process_kotlin_key_expr(&mut env, &key_expr_str_2, key_expr_ptr_2)?; + Ok(key_expr_1.intersects(&key_expr_2) as jboolean) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + false as jboolean + }) } +/// Returns true in case key_expr_1 includes key_expr_2. +/// +/// # Params: +/// - `key_expr_ptr_1`: Pointer to the key expression 1, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_1`: String representation of the key expression 1. +/// - `key_expr_ptr_2`: Pointer to the key expression 2, differs from null only if it's a declared key expr. +/// - `key_expr_ptr_2`: String representation of the key expression 2. +/// +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation, which happens only when providing +/// key expressions that were declared from a session (in that case the key expression has a pointer associated). +/// In that case, this function assumes the pointers are valid pointers to key expressions and those pointers +/// remain valid after the call to this function. +/// #[no_mangle] #[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_intersectsViaJNI( - _env: JNIEnv, +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_00024Companion_includesViaJNI( + mut env: JNIEnv, _: JClass, - key_expr_ptr_1: *const KeyExpr<'static>, - key_expr_ptr_2: *const KeyExpr<'static>, + key_expr_ptr_1: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_1: JString, + key_expr_ptr_2: /*nullable*/ *const KeyExpr<'static>, + key_expr_str_2: JString, ) -> jboolean { - let key_expr_1 = Arc::from_raw(key_expr_ptr_1); - let key_expr_2 = Arc::from_raw(key_expr_ptr_2); - let intersects = key_expr_1.intersects(&key_expr_2); - std::mem::forget(key_expr_1); - std::mem::forget(key_expr_2); - intersects as jboolean + || -> Result { + let key_expr_1 = process_kotlin_key_expr(&mut env, &key_expr_str_1, key_expr_ptr_1)?; + let key_expr_2 = process_kotlin_key_expr(&mut env, &key_expr_str_2, key_expr_ptr_2)?; + Ok(key_expr_1.includes(&key_expr_2) as jboolean) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + false as jboolean + }) } +/// Frees a declared key expression. +/// +/// # Parameters +/// - `_env`: Unused. The JNI environment. +/// - `_class`: Unused. The java class from which the function was called. +/// - `key_expr_ptr`: the pointer to the key expression. +/// +/// # Safety +/// - This function assumes the provided pointer is valid and points to a native key expression. +/// - The memory associated to the pointer is freed after returning from this call, turning the +/// pointer invalid after that. +/// #[no_mangle] #[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_includesViaJNI( +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_freePtrViaJNI( _env: JNIEnv, _: JClass, - key_expr_ptr_1: *const KeyExpr<'static>, - key_expr_ptr_2: *const KeyExpr<'static>, -) -> jboolean { - let key_expr_1 = Arc::from_raw(key_expr_ptr_1); - let key_expr_2 = Arc::from_raw(key_expr_ptr_2); - let includes = key_expr_1.includes(&key_expr_2); - std::mem::forget(key_expr_1); - std::mem::forget(key_expr_2); - includes as jboolean + key_expr_ptr: *const KeyExpr<'static>, +) { + Arc::from_raw(key_expr_ptr); } -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_equalsViaJNI( - _env: JNIEnv, - _: JClass, - key_expr_ptr_1: *const KeyExpr<'static>, - key_expr_ptr_2: *const KeyExpr<'static>, -) -> jboolean { - let key_expr_1 = Arc::from_raw(key_expr_ptr_1); - let key_expr_2 = Arc::from_raw(key_expr_ptr_2); - let is_equal = key_expr_1.eq(&key_expr_2); - std::mem::forget(key_expr_1); - std::mem::forget(key_expr_2); - is_equal as jboolean +fn decode_key_expr(env: &mut JNIEnv, key_expr: &JString) -> Result> { + let key_expr_str = decode_string(env, key_expr) + .map_err(|err| jni_error!("Unable to get key expression string value: '{}'.", err))?; + + KeyExpr::try_from(key_expr_str) + .map_err(|err| key_expr_error!("Unable to create key expression: '{}'.", err)) } -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - ptr: *const KeyExpr<'static>, -) { - Arc::from_raw(ptr); +fn autocanonize_key_expr(env: &mut JNIEnv, key_expr: &JString) -> Result> { + decode_string(env, key_expr) + .map_err(|err| jni_error!("Unable to get key expression string value: '{}'.", err)) + .and_then(|key_expr_str| { + KeyExpr::autocanonize(key_expr_str) + .map_err(|err| key_expr_error!("Unable to create key expression: '{}'", err)) + }) +} + +/// Processes a kotlin key expression. +/// +/// Receives the Java/Kotlin string representation of the key expression as well as a pointer. +/// The pointer is only valid in cases where the key expression is associated to a native pointer +/// (when the key expression was declared from a session). +/// If the pointer is valid, the key expression returned is the key expression the pointer pointed to. +/// Otherwise, a key expression created from the string representation of the key expression is returned. +/// +pub(crate) unsafe fn process_kotlin_key_expr( + env: &mut JNIEnv, + key_expr_str: &JString, + key_expr_ptr: *const KeyExpr<'static>, +) -> Result> { + if key_expr_ptr.is_null() { + decode_key_expr(env, key_expr_str) + .map_err(|err| jni_error!("Unable to process key expression: '{}'.", err)) + } else { + let key_expr = Arc::from_raw(key_expr_ptr); + let key_expr_clone = key_expr.deref().clone(); + std::mem::forget(key_expr); + Ok(key_expr_clone) + } } diff --git a/zenoh-jni/src/lib.rs b/zenoh-jni/src/lib.rs index 521d2c4..edfba47 100644 --- a/zenoh-jni/src/lib.rs +++ b/zenoh-jni/src/lib.rs @@ -16,15 +16,11 @@ mod errors; mod key_expr; mod logger; mod publisher; -mod put; mod query; mod queryable; -mod reply; -mod sample; mod session; mod subscriber; mod utils; -mod value; // Test should be runned with `cargo test --no-default-features` #[test] diff --git a/zenoh-jni/src/logger.rs b/zenoh-jni/src/logger.rs index ffdc0be..780e693 100644 --- a/zenoh-jni/src/logger.rs +++ b/zenoh-jni/src/logger.rs @@ -17,7 +17,10 @@ use jni::{ JNIEnv, }; -use crate::errors::{Error, Result}; +use crate::{ + errors::{Error, Result}, + jni_error, throw_exception, +}; /// Redirects the Rust logs either to logcat for Android systems or to the standard output (for non-Android systems). /// @@ -25,12 +28,12 @@ use crate::errors::{Error, Result}; /// indicating the desired log level, which must be one of the following: "info", "debug", "warn", /// "trace", or "error". /// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. /// - `log_level`: The log level java string indicating the desired log level. /// -/// Errors: +/// # Errors: /// - If there is an error parsing the log level string, a `JNIException` is thrown on the JVM. /// #[no_mangle] @@ -40,45 +43,22 @@ pub extern "C" fn Java_io_zenoh_Logger_00024Companion_start( _class: JClass, log_level: JString, ) { - let log_level = match parse_log_level(&mut env, log_level) { - Ok(level) => level, - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!("Error throwing exception on log start failure! {}", err) - }); - return; - } - }; - android_logd_logger::builder() - .parse_filters(log_level.as_str()) - .tag_target_strip() - .prepend_module(true) - .init(); + || -> Result<()> { + let log_level = parse_log_level(&mut env, log_level)?; + android_logd_logger::builder() + .parse_filters(log_level.as_str()) + .tag_target_strip() + .prepend_module(true) + .init(); + Ok(()) + }() + .unwrap_or_else(|err| throw_exception!(env, err)) } -/// Parses the log level string from the JNI environment into a Rust String. -/// -/// This function takes a mutable reference to the JNI environment (`env`) and a `log_level` -/// indicating the desired log level as a JString. It retrieves the log level string from the JNI -/// environment and converts it into a Rust String. -/// -/// Parameters: -/// - `env`: A mutable reference to the JNI environment. -/// - `log_level`: The log level as a JString. -/// -/// Returns: -/// - A [Result] containing the parsed log level string as a [String]. -/// -/// Errors: -/// - If there is an error retrieving or converting the log level string, a [Error::Jni] is returned -/// with the error message. -/// fn parse_log_level(env: &mut JNIEnv, log_level: JString) -> Result { - let log_level = env - .get_string(&log_level) - .map_err(|err| Error::Jni(err.to_string()))?; + let log_level = env.get_string(&log_level).map_err(|err| jni_error!(err))?; log_level .to_str() .map(|level| Ok(level.to_string())) - .map_err(|err| Error::Jni(err.to_string()))? + .map_err(|err| jni_error!(err))? } diff --git a/zenoh-jni/src/publisher.rs b/zenoh-jni/src/publisher.rs index 1f6cf54..15e59c2 100644 --- a/zenoh-jni/src/publisher.rs +++ b/zenoh-jni/src/publisher.rs @@ -12,47 +12,37 @@ // ZettaScale Zenoh Team, // -use std::{ops::Deref, sync::Arc}; +use std::sync::Arc; use jni::{ - objects::{JByteArray, JClass}, + objects::{JByteArray, JClass, JString}, sys::jint, JNIEnv, }; -use zenoh::{ - prelude::{sync::SyncResolve, KeyExpr}, - publication::Publisher, - Session, -}; +use zenoh::{bytes::EncodingBuilderTrait, pubsub::Publisher, sample::SampleBuilderTrait, Wait}; use crate::{ - errors::{Error, Result}, - utils::{decode_byte_array, vec_to_attachment}, -}; -use crate::{ - put::{decode_congestion_control, decode_priority}, - value::decode_value, + errors::Result, + utils::{decode_byte_array, decode_encoding}, }; -use zenoh::SessionDeclarations; +use crate::{session_error, throw_exception}; -/// Performs a put operation on a Zenoh publisher via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `payload`: The payload to be published, represented as a Java byte array (`JByteArray`). -/// - `encoding`: The encoding type of the payload. -/// - `encoded_attachment`: Optional encoded attachment. May be null. -/// - `ptr`: The raw pointer to the Zenoh publisher ([Publisher]). -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided publisher pointer is valid and has not been modified or freed. -/// - The ownership of the publisher is not transferred, and it is safe to continue using the publisher -/// after this function call. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. +/// Performs a PUT operation on a Zenoh publisher via JNI. +/// +/// # Parameters +/// - `env`: The JNI environment pointer. +/// - `_class`: The Java class reference (unused). +/// - `payload`: The byte array to be published. +/// - `encoding_id`: The encoding ID of the payload. +/// - `encoding_schema`: Nullable encoding schema string of the payload. +/// - `attachment`: Nullble byte array for the attachment. +/// - `publisher_ptr`: The raw pointer to the Zenoh publisher ([Publisher]). +/// +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. +/// - Assumes that the provided publisher pointer is valid and has not been modified or freed. +/// - The publisher pointer remains valid after this function call. +/// - May throw an exception in case of failure, which must be handled by the caller. /// #[no_mangle] #[allow(non_snake_case)] @@ -60,272 +50,80 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_putViaJNI( mut env: JNIEnv, _class: JClass, payload: JByteArray, - encoding: jint, - encoded_attachment: JByteArray, - ptr: *const Publisher<'static>, -) { - let publisher = Arc::from_raw(ptr); - match perform_put( - &env, - payload, - encoding, - encoded_attachment, - publisher.clone(), - ) { - Ok(_) => {} - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on PUT operation failure: {}", - err - ) - }); - } - }; - std::mem::forget(publisher) -} - -/// Frees the memory associated with a Zenoh publisher raw pointer via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `_env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh publisher ([Publisher]). -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation. -/// - It assumes that the provided publisher pointer is valid and has not been modified or freed. -/// - The function takes ownership of the raw pointer and releases the associated memory. -/// - After calling this function, the publisher pointer becomes invalid and should not be used anymore. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_freePtrViaJNI( - _env: JNIEnv, - _: JClass, - ptr: *const Publisher, -) { - Arc::from_raw(ptr); -} - -/// Declares a Zenoh publisher via JNI. -/// -/// Parameters: -/// - `key_expr_ptr`: Raw pointer to the key expression to be used for the publisher. -/// - `session_ptr`: Raw pointer to the Zenoh [Session] to be used for the publisher. -/// - `congestion_control`: The [zenoh::CongestionControl] configuration as an ordinal. -/// - `priority`: The [zenoh::Priority] configuration as an ordinal. -/// -/// Returns: -/// - A [Result] containing a raw pointer to the declared Zenoh publisher ([Publisher]) in case of success, -/// or an [Error] variant in case of failure. -/// -/// Safety: -/// - The returned raw pointer should be stored appropriately and later freed using [Java_io_zenoh_jni_JNIPublisher_freePtrViaJNI]. -/// -pub(crate) unsafe fn declare_publisher( - key_expr_ptr: *const KeyExpr<'static>, - session_ptr: *const Session, - congestion_control: jint, - priority: jint, -) -> Result<*const Publisher<'static>> { - let session = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr_ptr); - let key_expr_clone = key_expr.deref().clone(); - let congestion_control = decode_congestion_control(congestion_control)?; - let priority = decode_priority(priority)?; - let result = session - .declare_publisher(key_expr_clone.to_owned()) - .congestion_control(congestion_control) - .priority(priority) - .res(); - std::mem::forget(session); - std::mem::forget(key_expr); - match result { - Ok(publisher) => { - tracing::trace!("Declared publisher ok key expr '{key_expr_clone}', with congestion control '{congestion_control:?}', priority '{priority:?}'."); - Ok(Arc::into_raw(Arc::new(publisher))) - } - Err(err) => Err(Error::Session(err.to_string())), - } -} - -/// Performs a PUT operation via JNI using the specified Zenoh publisher. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `payload`: The payload as a `JByteArray`. -/// - `encoding`: The encoding of the payload. -/// - `encoded_attachment`: Optional encoded attachment. May be null. -/// - `publisher`: The Zenoh publisher. -/// -/// Returns: -/// - A [Result] indicating the success or failure of the operation. -/// -fn perform_put( - env: &JNIEnv, - payload: JByteArray, - encoding: jint, - encoded_attachment: JByteArray, - publisher: Arc, -) -> Result<()> { - let value = decode_value(env, payload, encoding)?; - let mut publication = publisher.put(value); - if !encoded_attachment.is_null() { - let aux = decode_byte_array(env, encoded_attachment)?; - publication = publication.with_attachment(vec_to_attachment(aux)) - }; - publication - .res_sync() - .map_err(|err| Error::Session(err.to_string())) -} - -/// Modifies the congestion control policy of a running Publisher through JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The Publisher class (unused but required). -/// - `congestion_control`: The [zenoh::CongestionControl] value to be set. -/// - `ptr`: Pointer to the publisher. -/// -/// Safety: -/// - This function is maked as unsafe due to raw pointer manipulation. -/// - This function is NOT thread safe; if there were to be multiple threads calling this function -/// concurrently while providing the same Publisher pointer, the result will be non deterministic. -/// -/// Throws: -/// - An exception in case the congestion control fails to be decoded. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_setCongestionControlViaJNI( - env: &mut JNIEnv, - _class: JClass, - congestion_control: jint, - ptr: *const Publisher<'static>, + encoding_id: jint, + encoding_schema: /*nullable*/ JString, + attachment: /*nullable*/ JByteArray, + publisher_ptr: *const Publisher<'static>, ) { - let congestion_control = match decode_congestion_control(congestion_control) { - Ok(congestion_control) => congestion_control, - Err(err) => { - _ = err.throw_on_jvm(env); - return; - } - }; - tracing::debug!("Setting publisher congestion control with '{congestion_control:?}'."); - unsafe { - let publisher = core::ptr::read(ptr); - core::ptr::write( - ptr as *mut _, - publisher.congestion_control(congestion_control), - ); - } + let publisher = Arc::from_raw(publisher_ptr); + let _ = || -> Result<()> { + let payload = decode_byte_array(&env, payload)?; + let mut publication = publisher.put(payload); + let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; + publication = publication.encoding(encoding); + if !attachment.is_null() { + let attachment = decode_byte_array(&env, attachment)?; + publication = publication.attachment::>(attachment) + }; + publication.wait().map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); + std::mem::forget(publisher); } -/// Modifies the priority policy of a running Publisher through JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// Performs a DELETE operation on a Zenoh publisher via JNI. /// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The Publisher class (unused but required). -/// - `priority`: The [zenoh::Priority] value to be set. -/// - `ptr`: Pointer to the publisher. +/// # Parameters +/// - `env`: The JNI environment pointer. +/// - `_class`: The Java class reference (unused). +/// - `attachment`: Nullble byte array for the attachment. +/// - `publisher_ptr`: The raw pointer to the Zenoh publisher ([Publisher]). /// -/// Safety: -/// - This function is maked as unsafe due to raw pointer manipulation. -/// - This function is NOT thread safe; if there were to be multiple threads calling this function -/// concurrently while providing the same Publisher pointer, the result will be non deterministic. -/// -/// Throws: -/// - An exception in case the priority fails to be decoded. +/// # Safety +/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. +/// - Assumes that the provided publisher pointer is valid and has not been modified or freed. +/// - The publisher pointer remains valid after this function call. +/// - May throw an exception in case of failure, which must be handled by the caller. /// #[no_mangle] #[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_setPriorityViaJNI( - env: &mut JNIEnv, +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_deleteViaJNI( + mut env: JNIEnv, _class: JClass, - priority: jint, - ptr: *const Publisher<'static>, + attachment: /*nullable*/ JByteArray, + publisher_ptr: *const Publisher<'static>, ) { - let priority = match decode_priority(priority) { - Ok(priority) => priority, - Err(err) => { - _ = err.throw_on_jvm(env); - return; - } - }; - tracing::debug!("Setting publisher priority with '{priority:?}'."); - unsafe { - let publisher = core::ptr::read(ptr); - core::ptr::write(ptr as *mut _, publisher.priority(priority)); - } -} - -/// Performs a DELETE operation via JNI using the specified Zenoh publisher. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `encoded_attachment`: Optional encoded attachment. May be null. -/// - `publisher`: The Zenoh [Publisher]. -/// -/// Returns: -/// - A [Result] indicating the success or failure of the operation. -/// -fn perform_delete( - env: &JNIEnv, - encoded_attachment: JByteArray, - publisher: Arc, -) -> Result<()> { - let mut delete = publisher.delete(); - if !encoded_attachment.is_null() { - let aux = decode_byte_array(env, encoded_attachment)?; - delete = delete.with_attachment(vec_to_attachment(aux)) - }; - delete - .res() - .map_err(|err| Error::Session(format!("{}", err))) + let publisher = Arc::from_raw(publisher_ptr); + let _ = || -> Result<()> { + let mut delete = publisher.delete(); + if !attachment.is_null() { + let attachment = decode_byte_array(&env, attachment)?; + delete = delete.attachment::>(attachment) + }; + delete.wait().map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); + std::mem::forget(publisher) } -/// Performs a DELETE operation on a Zenoh publisher via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// Frees the publisher. /// -/// Parameters: -/// - `env`: The JNI environment. +/// # Parameters: +/// - `_env`: The JNI environment. /// - `_class`: The JNI class. -/// - `encoded_attachment`: Optional encoded attachment. May be null. -/// - `ptr`: The raw pointer to the [Zenoh publisher](Publisher). +/// - `publisher_ptr`: The raw pointer to the Zenoh publisher ([Publisher]). /// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. +/// # Safety: +/// - The function is marked as unsafe due to raw pointer manipulation. /// - It assumes that the provided publisher pointer is valid and has not been modified or freed. -/// - The ownership of the publisher is not transferred, and it is safe to continue using the publisher -/// after this function call. -/// - The function may throw an exception in case of failure, which should be handled by the caller. +/// - After calling this function, the publisher pointer becomes invalid and should not be used anymore. /// #[no_mangle] #[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_deleteViaJNI( - mut env: JNIEnv, - _class: JClass, - encoded_attachment: JByteArray, - ptr: *const Publisher<'static>, +pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_freePtrViaJNI( + _env: JNIEnv, + _: JClass, + publisher_ptr: *const Publisher, ) { - let publisher = Arc::from_raw(ptr); - match perform_delete(&env, encoded_attachment, publisher.clone()) { - Ok(_) => {} - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on WRITE operation failure: {}", - err - ) - }); - } - }; - std::mem::forget(publisher) + Arc::from_raw(publisher_ptr); } diff --git a/zenoh-jni/src/put.rs b/zenoh-jni/src/put.rs deleted file mode 100644 index e0cbbe1..0000000 --- a/zenoh-jni/src/put.rs +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use crate::errors::{Error, Result}; -use crate::sample::decode_sample_kind; -use crate::utils::{decode_byte_array, vec_to_attachment}; -use crate::value::decode_value; - -use jni::objects::JByteArray; -use jni::sys::jint; -use jni::JNIEnv; -use std::ops::Deref; -use std::sync::Arc; -use zenoh::prelude::r#sync::*; - -/// Performs a `put` operation in the Zenoh session, propagating eventual errors. -/// -/// Parameters: -/// - `env`: A mutable reference to the JNI environment. -/// - `key_expr`: The [KeyExpr] to use for the put operation. -/// - `session`: An [Session] to use for the put operation. -/// - `payload`: The payload to send through the network. -/// - `encoding`: The [Encoding] of the put operation. -/// - `congestion_control`: The [CongestionControl] mechanism specified. -/// - `priority`: The [Priority] mechanism specified. -/// - `sample_kind`: The [SampleKind] of the put operation. -/// - `attachment`: An optional attachment, encoded into a byte array. May be null. -/// -/// Returns: -/// - A `Result` indicating the result of the `get` operation, with an [Error] in case of failure. -/// -#[allow(clippy::too_many_arguments)] -pub(crate) fn on_put( - env: &mut JNIEnv, - key_expr: &Arc>, - session: &Arc, - payload: JByteArray, - encoding: jint, - congestion_control: jint, - priority: jint, - sample_kind: jint, - attachment: JByteArray, -) -> Result<()> { - let value = decode_value(env, payload, encoding)?; - let sample_kind = decode_sample_kind(sample_kind)?; - let congestion_control = match decode_congestion_control(congestion_control) { - Ok(congestion_control) => congestion_control, - Err(err) => { - tracing::warn!( - "Error decoding congestion control: '{}'. Using default...", - err - ); - CongestionControl::default() - } - }; - - let priority = match decode_priority(priority) { - Ok(priority) => priority, - Err(err) => { - tracing::warn!("Error decoding priority: '{}'. Using default...", err); - Priority::default() - } - }; - - let key_expr_clone = key_expr.deref().clone(); - - let mut put_builder = session - .put(key_expr_clone, value.to_owned()) - .kind(sample_kind) - .congestion_control(congestion_control) - .priority(priority); - - if !attachment.is_null() { - let attachment = decode_byte_array(env, attachment)?; - put_builder = put_builder.with_attachment(vec_to_attachment(attachment)) - } - - match put_builder.res() { - Ok(_) => { - tracing::trace!("Put on '{key_expr}' with value '{value}' and encoding '{}'. Kind: '{sample_kind}', Congestion control: '{congestion_control:?}', Priority: '{priority:?}'", value.encoding); - Ok(()) - } - Err(err) => Err(Error::Session(format!("{}", err))), - } -} - -pub(crate) fn decode_priority(priority: jint) -> Result { - match Priority::try_from(priority as u8) { - Ok(priority) => Ok(priority), - Err(err) => Err(Error::Session(format!("Error retrieving priority: {err}."))), - } -} - -pub(crate) fn decode_congestion_control(congestion_control: jint) -> Result { - match congestion_control { - 0 => Ok(CongestionControl::Block), - 1 => Ok(CongestionControl::Drop), - _value => Err(Error::Session(format!( - "Unknown congestion control '{_value}'." - ))), - } -} diff --git a/zenoh-jni/src/query.rs b/zenoh-jni/src/query.rs index 3af7122..fae0dd5 100644 --- a/zenoh-jni/src/query.rs +++ b/zenoh-jni/src/query.rs @@ -12,51 +12,51 @@ // ZettaScale Zenoh Team, // -use std::{mem, ops::Deref, sync::Arc}; +use std::sync::Arc; +use crate::{errors::Result, key_expr::process_kotlin_key_expr, throw_exception}; +use crate::{ + session_error, + utils::{decode_byte_array, decode_encoding}, +}; use jni::{ - objects::{GlobalRef, JByteArray, JClass, JPrimitiveArray, JValue}, + objects::{JByteArray, JClass, JString}, sys::{jboolean, jint, jlong}, JNIEnv, }; +use uhlc::ID; use zenoh::{ - prelude::{sync::SyncResolve, KeyExpr, SplitBuffer}, - query::{ConsolidationMode, QueryTarget}, - queryable::Query, - sample::{Attachment, Sample}, - value::Value, + key_expr::KeyExpr, + prelude::{EncodingBuilderTrait, Wait}, + qos::{CongestionControl, Priority, QoSBuilderTrait}, + query::Query, + sample::{SampleBuilderTrait, TimestampBuilderTrait}, + time::{Timestamp, NTP64}, }; -use crate::{ - errors::{Error, Result}, - utils::attachment_to_vec, - value::decode_value, -}; -use crate::{ - sample::decode_sample, - utils::{decode_byte_array, vec_to_attachment}, -}; - -/// Replies with success to a Zenoh query via JNI. +/// Replies with `success` to a Zenoh [Query] via JNI, freeing the query in the process. /// -/// This function is meant to be called from Java/Kotlin through JNI. -/// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh query. -/// - `key_expr`: The key expression associated with the query result. -/// - `payload`: The payload as a `JByteArray`. -/// - `encoding`: The encoding of the payload. -/// - `sample_kind`: The kind of sample. +/// - `query_ptr`: The raw pointer to the Zenoh query. +/// - `key_expr_ptr`: Nullable key expression pointer associated with the query result. This parameter +/// is meant to be used with declared key expressions, which have a pointer associated to them. +/// In case of it being null, then the `key_expr_string` will be used to perform the reply. +/// - `key_expr_str`: The string representation of the key expression associated with the query result. +/// - `payload`: The payload for the reply. +/// - `encoding_id`: The encoding id of the payload. +/// - `encoding_schema`: Nullable encoding schema. /// - `timestamp_enabled`: A boolean indicating whether the timestamp is enabled. /// - `timestamp_ntp_64`: The NTP64 timestamp value. -/// - `attachment`: Optional user attachment encoded as a byte array. May be null. +/// - `attachment`: Nullable user attachment encoded as a byte array. +/// - `qos_*`: QoS parameters for the reply. /// -/// Safety: +/// # Safety: /// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. /// - It assumes that the provided raw pointer to the Zenoh query is valid and has not been modified or freed. -/// - The ownership of the Zenoh query is not transferred, and it remains valid after this call. +/// - The query pointer is freed after calling this function (queries shouldn't be replied more than once), +/// therefore the query isn't valid anymore after that. /// - May throw a JNI exception in case of failure, which should be handled by the caller. /// #[no_mangle] @@ -64,119 +64,145 @@ use crate::{ pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replySuccessViaJNI( mut env: JNIEnv, _class: JClass, - query_ptr: *const zenoh::queryable::Query, - key_expr_ptr: *const KeyExpr<'static>, + query_ptr: *const Query, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, payload: JByteArray, - encoding: jint, - sample_kind: jint, + encoding_id: jint, + encoding_schema: /*nullable*/ JString, timestamp_enabled: jboolean, timestamp_ntp_64: jlong, - attachment: JByteArray, + attachment: /*nullable*/ JByteArray, + qos_express: jboolean, + qos_priority: jint, + qos_congestion_control: jint, ) { - let key_expr = Arc::from_raw(key_expr_ptr); - let key_expr_clone = key_expr.deref().clone(); - std::mem::forget(key_expr); - let sample = match decode_sample( - &mut env, - key_expr_clone, - payload, - encoding, - sample_kind, - timestamp_enabled, - timestamp_ntp_64, - ) { - Ok(sample) => sample, - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!("Unable to throw exception on query reply failure. {}", err) - }); - return; + let _ = || -> Result<()> { + let query = Arc::from_raw(query_ptr); + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let payload = decode_byte_array(&env, payload)?; + let mut reply_builder = query.reply(key_expr, payload); + let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; + reply_builder = reply_builder.encoding(encoding); + if timestamp_enabled != 0 { + let ts = Timestamp::new(NTP64(timestamp_ntp_64 as u64), ID::rand()); + reply_builder = reply_builder.timestamp(ts) } - }; - let attachment: Option = if !attachment.is_null() { - match decode_byte_array(&env, attachment) { - Ok(attachment_bytes) => Some(vec_to_attachment(attachment_bytes)), - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!("Unable to throw exception on query reply failure. {}", err) - }); - return; - } + if !attachment.is_null() { + reply_builder = reply_builder.attachment(decode_byte_array(&env, attachment)?); } - } else { - None - }; - - let query = Arc::from_raw(query_ptr); - query_reply(env, &query, Ok(sample), attachment); - mem::forget(query) + reply_builder = reply_builder.express(qos_express != 0); + reply_builder = reply_builder.priority(Priority::try_from(qos_priority as u8).unwrap()); // The numeric value is always within range. + reply_builder = if qos_congestion_control != 0 { + reply_builder.congestion_control(CongestionControl::Block) + } else { + reply_builder.congestion_control(CongestionControl::Drop) + }; + reply_builder.wait().map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); } -/// Replies with error to a Zenoh query via JNI. -/// -/// This function is meant to be called from Java/Kotlin through JNI. -/// -/// Support: -/// - Replying with error is a feature that is not yet supported by Zenoh. This implementation is -/// meant to prepare the API to these expected changes. Calling this function now would cause an -/// exception. +/// Replies with `error` to a Zenoh [Query] via JNI, freeing the query in the process. /// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh query. -/// - `key_expr`: The key expression associated with the query result. -/// - `payload`: The payload as a `JByteArray`. -/// - `encoding`: The encoding of the payload as a jint. -/// - `attachment`: The user attachment bytes. +/// - `query_ptr`: The raw pointer to the Zenoh query. +/// - `payload`: The payload for the reply. +/// - `encoding_id`: The encoding id of the payload. +/// - `encoding_schema`: Nullable encoding schema. /// -/// Safety: +/// # Safety: /// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. /// - It assumes that the provided raw pointer to the Zenoh query is valid and has not been modified or freed. -/// - The ownership of the Zenoh query is not transferred, and it remains valid after this call. /// - May throw a JNI exception in case of failure, which should be handled by the caller. +/// - The query pointer is freed after calling this function (queries shouldn't be replied more than once), +/// therefore the query isn't valid anymore after that. /// #[no_mangle] #[allow(non_snake_case)] pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replyErrorViaJNI( mut env: JNIEnv, _class: JClass, - ptr: *const zenoh::queryable::Query, + query_ptr: *const Query, payload: JByteArray, - encoding: jint, - attachment: JByteArray, + encoding_id: jint, + encoding_schema: /*nullable*/ JString, ) { - let errorValue = match decode_value(&env, payload, encoding) { - Ok(value) => value, - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!("Unable to throw exception on query reply failure. {}", err) - }); - return; + let _ = || -> Result<()> { + let query = Arc::from_raw(query_ptr); + let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; + query + .reply_err(decode_byte_array(&env, payload)?) + .encoding(encoding) + .wait() + .map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); +} + +/// Replies with `delete` to a Zenoh [Query] via JNI, freeing the query in the process. +/// +/// # Parameters: +/// - `env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `query_ptr`: The raw pointer to the Zenoh query. +/// - `key_expr_ptr`: Nullable key expression pointer associated with the query result. This parameter +/// is meant to be used with declared key expressions, which have a pointer associated to them. +/// In case of it being null, then the `key_expr_string` will be used to perform the reply. +/// - `key_expr_str`: The string representation of the key expression associated with the query result. +/// - `timestamp_enabled`: A boolean indicating whether the timestamp is enabled. +/// - `timestamp_ntp_64`: The NTP64 timestamp value. +/// - `attachment`: Nullable user attachment encoded as a byte array. +/// - `qos_*`: QoS parameters for the reply. +/// +/// # Safety: +/// - This function is marked as unsafe due to raw pointer manipulation and JNI interaction. +/// - It assumes that the provided raw pointer to the Zenoh query is valid and has not been modified or freed. +/// - May throw a JNI exception in case of failure, which should be handled by the caller. +/// - The query pointer is freed after calling this function (queries shouldn't be replied more than once), +/// therefore the query isn't valid anymore after that. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replyDeleteViaJNI( + mut env: JNIEnv, + _class: JClass, + query_ptr: *const Query, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, + timestamp_enabled: jboolean, + timestamp_ntp_64: jlong, + attachment: /*nullable*/ JByteArray, + qos_express: jboolean, + qos_priority: jint, + qos_congestion_control: jint, +) { + let _ = || -> Result<()> { + let query = Arc::from_raw(query_ptr); + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let mut reply_builder = query.reply_del(key_expr); + if timestamp_enabled != 0 { + let ts = Timestamp::new(NTP64(timestamp_ntp_64 as u64), ID::rand()); + reply_builder = reply_builder.timestamp(ts) } - }; - let attachment: Option = if !attachment.is_null() { - match decode_byte_array(&env, attachment) { - Ok(attachment_bytes) => Some(vec_to_attachment(attachment_bytes)), - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!("Unable to throw exception on query reply failure. {}", err) - }); - return; - } + if !attachment.is_null() { + reply_builder = reply_builder.attachment(decode_byte_array(&env, attachment)?); } - } else { - None - }; - - let query = Arc::from_raw(ptr); - query_reply(env, &query, Err(errorValue), attachment); - mem::forget(query) + reply_builder = reply_builder.express(qos_express != 0); + reply_builder = reply_builder.priority(Priority::try_from(qos_priority as u8).unwrap()); // The numeric value is always within range. + reply_builder = if qos_congestion_control != 0 { + reply_builder.congestion_control(CongestionControl::Block) + } else { + reply_builder.congestion_control(CongestionControl::Drop) + }; + reply_builder.wait().map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); } -/// Frees the memory associated with a Zenoh query raw pointer via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// Frees the Query via JNI. /// /// Parameters: /// - `_env`: The JNI environment. @@ -194,162 +220,7 @@ pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_replyErrorViaJNI( pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQuery_freePtrViaJNI( _env: JNIEnv, _: JClass, - ptr: *const zenoh::queryable::Query, -) { - Arc::from_raw(ptr); -} - -/// Handles a Zenoh query callback via JNI. -/// -/// This function is responsible for invoking the query callback function provided by the user from Java/Kotlin. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `query`: The Zenoh [Query] to be passed to the callback function. -/// - `callback_global_ref`: A global object reference of the callback function. -/// -/// Returns: -/// - A [Result] indicating success or failure. -/// -/// Note: -/// - The callback reference `callback_global_ref` should point to the Java/Kotlin implementation -/// of the `onQuery` function (which receives a `Query` as a parameter) from the `Callback` interface. -/// -pub(crate) fn on_query( - mut env: JNIEnv, - query: Query, - callback_global_ref: &GlobalRef, -) -> Result<()> { - let selector = query.selector(); - let value = query.value(); - - let selector_params_jstr = - env.new_string(selector.parameters().to_string()) - .map_err(|err| { - Error::Jni(format!( - "Could not create a JString through JNI for the Query key expression. {}", - err - )) - })?; - - let (with_value, payload, encoding) = match value { - Some(value) => { - let byte_array = env - .byte_array_from_slice(value.payload.contiguous().as_ref()) - .map_err(|err| Error::Jni(err.to_string()))?; - let encoding: i32 = value.encoding.prefix().to_owned() as i32; - (true, byte_array, encoding) - } - None => (false, JPrimitiveArray::default(), 0), - }; - - let attachment_bytes = match query.attachment().map_or_else( - || env.byte_array_from_slice(&[]), - |attachment| env.byte_array_from_slice(attachment_to_vec(attachment.clone()).as_slice()), - ) { - Ok(byte_array) => Ok(byte_array), - Err(err) => Err(Error::Jni(format!( - "Error processing attachment of reply: {}.", - err - ))), - }?; - - let key_expr = query.key_expr().clone(); - let key_expr_ptr = Arc::into_raw(Arc::new(key_expr)); - let query_ptr = Arc::into_raw(Arc::new(query)); - - let result = env - .call_method( - callback_global_ref, - "run", - "(JLjava/lang/String;Z[BI[BJ)V", - &[ - JValue::from(key_expr_ptr as jlong), - JValue::from(&selector_params_jstr), - JValue::from(with_value), - JValue::from(&payload), - JValue::from(encoding), - JValue::from(&attachment_bytes), - JValue::from(query_ptr as jlong), - ], - ) - .map(|_| ()) - .map_err(|err| { - // The callback could not be invoked, therefore the created kotlin query object won't be - // used. Since `query_ptr` as well as `key_expr_ptr` was created within this function - // and remains unaltered, it is safe to reclaim ownership of the memory by converting - // the raw pointers back into an `Arc` and freeing the memory. - unsafe { - Arc::from_raw(query_ptr); - Arc::from_raw(key_expr_ptr) - }; - _ = env.exception_describe(); - Error::Jni(format!("{}", err)) - }); - - _ = env - .delete_local_ref(selector_params_jstr) - .map_err(|err| tracing::error!("Error deleting local ref: {}", err)); - _ = env - .delete_local_ref(payload) - .map_err(|err| tracing::error!("Error deleting local ref: {}", err)); - _ = env - .delete_local_ref(attachment_bytes) - .map_err(|err| tracing::error!("Error deleting local ref: {}", err)); - result -} - -/// Helper function to perform a reply to a query. -fn query_reply( - mut env: JNIEnv, - query: &Arc, - reply: core::result::Result, - attachment: Option, + query_ptr: *const Query, ) { - let result = if let Some(attachment) = attachment { - query - .reply(reply) - .with_attachment(attachment) - .unwrap_or_else(|(builder, _)| { - tracing::warn!("Unable to append attachment to query reply"); - builder - }) - .res() - .map_err(|err| Error::Session(err.to_string())) - } else { - query - .reply(reply) - .res() - .map_err(|err| Error::Session(err.to_string())) - }; - match result { - Ok(_) => {} - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!("Unable to throw exception on query reply failure. {}", err) - }); - } - } -} - -pub(crate) fn decode_query_target(target: jint) -> Result { - match target { - 0 => Ok(QueryTarget::BestMatching), - 1 => Ok(QueryTarget::All), - 2 => Ok(QueryTarget::AllComplete), - value => Err(Error::Session(format!( - "Unable to decode QueryTarget {value}" - ))), - } -} - -pub(crate) fn decode_consolidation(consolidation: jint) -> Result { - match consolidation { - 0 => Ok(ConsolidationMode::None), - 1 => Ok(ConsolidationMode::Monotonic), - 2 => Ok(ConsolidationMode::Latest), - value => Err(Error::Session(format!( - "Unable to decode consolidation {value}" - ))), - } + Arc::from_raw(query_ptr); } diff --git a/zenoh-jni/src/queryable.rs b/zenoh-jni/src/queryable.rs index ab51f4e..07ed6e1 100644 --- a/zenoh-jni/src/queryable.rs +++ b/zenoh-jni/src/queryable.rs @@ -12,33 +12,19 @@ // ZettaScale Zenoh Team, // -use std::{ops::Deref, sync::Arc}; +use std::sync::Arc; -use jni::{ - objects::{JClass, JObject}, - sys::jboolean, - JNIEnv, -}; -use zenoh::prelude::r#sync::*; -use zenoh::{queryable::Queryable, Session}; +use jni::{objects::JClass, JNIEnv}; +use zenoh::query::Queryable; -use crate::{ - errors::Error, - errors::Result, - query::on_query, - utils::{get_callback_global_ref, get_java_vm, load_on_close}, -}; - -/// Frees the memory associated with a Zenoh queryable raw pointer via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// Frees the [Queryable]. /// -/// Parameters: +/// # Parameters: /// - `_env`: The JNI environment. /// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh queryable ([Queryable]). +/// - `queryable_ptr`: The raw pointer to the Zenoh queryable ([Queryable]). /// -/// Safety: +/// # Safety: /// - The function is marked as unsafe due to raw pointer manipulation. /// - It assumes that the provided queryable pointer is valid and has not been modified or freed. /// - The function takes ownership of the raw pointer and releases the associated memory. @@ -49,70 +35,7 @@ use crate::{ pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIQueryable_freePtrViaJNI( _env: JNIEnv, _: JClass, - ptr: *const zenoh::queryable::Queryable<'_, ()>, + queryable_ptr: *const Queryable<'_, ()>, ) { - Arc::from_raw(ptr); -} - -/// Declares a Zenoh queryable via JNI. -/// -/// Parameters: -/// - `env`: A mutable reference to the JNI environment. -/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the queryable. -/// - `session_ptr`: Raw pointer to the [Session] from which to declare the queryable.. -/// - `callback`: The callback function as an instance of the `Callback` interface in Java/Kotlin. -/// - `on_close`: The `on_close` callback function as an instance of the `JNIOnCloseCallback` interface in -/// Java/Kotlin, to be called when Zenoh notfies that no more queries will be received. -/// - `complete`: The completeness of the queryable. -/// -/// Returns: -/// - A [Result] containing a raw pointer to the declared Zenoh queryable ([Queryable]) in case of success, -/// or an [Error] variant in case of failure. -/// -/// Safety: -/// - The returned raw pointer should be stored appropriately and later freed using [Java_io_zenoh_jni_JNIQueryable_freePtrViaJNI]. -/// -pub(crate) unsafe fn declare_queryable( - env: &mut JNIEnv, - key_expr_ptr: *const KeyExpr<'static>, - session_ptr: *const zenoh::Session, - callback: JObject, - on_close: JObject, - complete: jboolean, -) -> Result> { - let java_vm = Arc::new(get_java_vm(env)?); - let callback_global_ref = get_callback_global_ref(env, callback)?; - let on_close_global_ref = get_callback_global_ref(env, on_close)?; - let complete = complete != 0; - let on_close = load_on_close(&java_vm, on_close_global_ref); - - let session: Arc = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr_ptr); - let key_expr_clone = key_expr.deref().clone(); - tracing::debug!("Declaring queryable through JNI on {}", key_expr); - let queryable = session - .declare_queryable(key_expr_clone) - .callback(move |query| { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - let env = match java_vm.attach_current_thread_as_daemon() { - Ok(env) => env, - Err(err) => { - tracing::error!("Unable to attach thread for queryable callback: {}", err); - return; - } - }; - - tracing::debug!("Receiving query through JNI: {}", query.to_string()); - match on_query(env, query, &callback_global_ref) { - Ok(_) => tracing::debug!("Queryable callback called successfully."), - Err(err) => tracing::error!("Error calling queryable callback: {}", err), - } - }) - .complete(complete); - - std::mem::forget(session); - std::mem::forget(key_expr); - queryable - .res() - .map_err(|err| Error::Session(format!("Error declaring queryable: {}", err))) + Arc::from_raw(queryable_ptr); } diff --git a/zenoh-jni/src/reply.rs b/zenoh-jni/src/reply.rs deleted file mode 100644 index ad5a023..0000000 --- a/zenoh-jni/src/reply.rs +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use std::sync::Arc; - -use jni::{ - objects::{GlobalRef, JObject, JValue}, - sys::{jint, jlong}, - JNIEnv, -}; -use zenoh::{ - prelude::{SplitBuffer, ZenohId}, - query::Reply, - sample::Sample, - value::Value, -}; - -use crate::errors::Result; -use crate::{errors::Error, utils::attachment_to_vec}; - -pub(crate) fn on_reply( - mut env: JNIEnv, - reply: Reply, - callback_global_ref: &GlobalRef, -) -> Result<()> { - match reply.sample { - Ok(sample) => on_reply_success(&mut env, reply.replier_id, sample, callback_global_ref), - Err(value) => on_reply_error(&mut env, reply.replier_id, value, callback_global_ref), - } -} - -fn on_reply_success( - env: &mut JNIEnv, - replier_id: ZenohId, - sample: Sample, - callback_global_ref: &GlobalRef, -) -> Result<()> { - let zenoh_id = env - .new_string(replier_id.to_string()) - .map_err(|err| Error::Jni(err.to_string()))?; - let byte_array = env - .byte_array_from_slice(sample.value.payload.contiguous().as_ref()) - .map_err(|err| Error::Jni(err.to_string()))?; - let encoding: jint = sample.value.encoding.prefix().to_owned() as jint; - let kind = sample.kind.to_owned() as jint; - let (timestamp, is_valid) = sample.timestamp.map_or_else( - || (0, false), - |timestamp| (timestamp.get_time().as_u64(), true), - ); - - let attachment_bytes = match sample.attachment.map_or_else( - || env.byte_array_from_slice(&[]), - |attachment| env.byte_array_from_slice(attachment_to_vec(attachment).as_slice()), - ) { - Ok(byte_array) => Ok(byte_array), - Err(err) => Err(Error::Jni(format!( - "Error processing attachment of reply: {}.", - err - ))), - }?; - - let key_expr_ptr = Arc::into_raw(Arc::new(sample.key_expr)); - let result = match env.call_method( - callback_global_ref, - "run", - "(Ljava/lang/String;ZJ[BIIJZ[B)V", - &[ - JValue::from(&zenoh_id), - JValue::from(true), - JValue::from(key_expr_ptr as jlong), - JValue::from(&byte_array), - JValue::from(encoding), - JValue::from(kind), - JValue::from(timestamp as i64), - JValue::from(is_valid), - JValue::from(&attachment_bytes), - ], - ) { - Ok(_) => Ok(()), - Err(err) => { - unsafe { - Arc::from_raw(key_expr_ptr); - } - _ = env.exception_describe(); - Err(Error::Jni(format!("On GET callback error: {}", err))) - } - }; - - _ = env - .delete_local_ref(zenoh_id) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - _ = env - .delete_local_ref(byte_array) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - _ = env - .delete_local_ref(attachment_bytes) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - result -} - -fn on_reply_error( - env: &mut JNIEnv, - replier_id: ZenohId, - value: Value, - callback_global_ref: &GlobalRef, -) -> Result<()> { - let zenoh_id = env - .new_string(replier_id.to_string()) - .map_err(|err| Error::Jni(err.to_string()))?; - - let byte_array = env - .byte_array_from_slice(value.payload.contiguous().as_ref()) - .map_err(|err| Error::Jni(err.to_string()))?; - let encoding: jint = value.encoding.prefix().to_owned() as jint; - - let result = match env.call_method( - callback_global_ref, - "run", - "(Ljava/lang/String;ZJ[BIIJZ)V", - &[ - JValue::from(&zenoh_id), - JValue::from(false), - JValue::from(&JObject::null()), - JValue::from(&byte_array), - JValue::from(encoding), - JValue::from(0), - JValue::from(0), - JValue::from(false), - ], - ) { - Ok(_) => Ok(()), - Err(err) => { - _ = env.exception_describe(); - Err(Error::Jni(format!("On GET callback error: {}", err))) - } - }; - - _ = env - .delete_local_ref(zenoh_id) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - _ = env - .delete_local_ref(byte_array) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - result -} diff --git a/zenoh-jni/src/sample.rs b/zenoh-jni/src/sample.rs deleted file mode 100644 index 1d8dd23..0000000 --- a/zenoh-jni/src/sample.rs +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use crate::{ - errors::{Error, Result}, - value::decode_value, -}; -use jni::{ - objects::JByteArray, - sys::{jboolean, jint, jlong}, - JNIEnv, -}; -use uhlc::{Timestamp, ID, NTP64}; -use zenoh::{ - prelude::{KeyExpr, SampleKind}, - sample::Sample, -}; - -/// Attempts to reconstruct a Zenoh [Sample] from the Java/Kotlin fields specified. -pub(crate) fn decode_sample( - env: &mut JNIEnv, - key_expr: KeyExpr<'static>, - payload: JByteArray, - encoding: jint, - sample_kind: jint, - timestamp_enabled: jboolean, - timestamp_ntp_64: jlong, -) -> Result { - let value = decode_value(env, payload, encoding)?; - let mut sample = Sample::new(key_expr, value); - sample.kind = decode_sample_kind(sample_kind)?; - sample.timestamp = if timestamp_enabled != 0 { - Some(Timestamp::new(NTP64(timestamp_ntp_64 as u64), ID::rand())) - } else { - None - }; - Ok(sample) -} - -/// Converts a Java/Kotlin Integer into a [SampleKind]. -pub(crate) fn decode_sample_kind(sample_kind: jint) -> Result { - match SampleKind::try_from(sample_kind as u64) { - Ok(kind) => Ok(kind), - Err(sample_kind) => Err(Error::Jni(format!( - "Unable to process sample kind {}.", - sample_kind, - ))), - } -} diff --git a/zenoh-jni/src/session.rs b/zenoh-jni/src/session.rs index 46b6279..2391261 100644 --- a/zenoh-jni/src/session.rs +++ b/zenoh-jni/src/session.rs @@ -13,81 +13,86 @@ // use crate::errors::{Error, Result}; -use crate::publisher::declare_publisher; -use crate::put::on_put; -use crate::query::{decode_consolidation, decode_query_target}; -use crate::queryable::declare_queryable; -use crate::reply::on_reply; -use crate::subscriber::declare_subscriber; -use crate::utils::{ - decode_byte_array, decode_string, get_callback_global_ref, get_java_vm, load_on_close, - vec_to_attachment, -}; -use crate::value::decode_value; - -use jni::objects::{JByteArray, JClass, JObject, JString}; +use crate::key_expr::process_kotlin_key_expr; +use crate::{jni_error, utils::*}; +use crate::{session_error, throw_exception}; + +use jni::objects::{GlobalRef, JByteArray, JClass, JObject, JString, JValue}; use jni::sys::{jboolean, jint, jlong}; use jni::JNIEnv; +use std::mem; use std::ops::Deref; use std::ptr::null; use std::sync::Arc; use std::time::Duration; -use zenoh::config::Config; -use zenoh::prelude::r#sync::*; +use zenoh::config::{Config, ZenohId}; +use zenoh::key_expr::KeyExpr; +use zenoh::prelude::{EncodingBuilderTrait, Wait}; +use zenoh::pubsub::{Publisher, Subscriber}; +use zenoh::qos::QoSBuilderTrait; +use zenoh::query::{Query, Queryable, ReplyError, Selector}; +use zenoh::sample::{Sample, SampleBuilderTrait}; +use zenoh::session::{Session, SessionDeclarations}; /// Open a Zenoh session via JNI. /// -/// This function is meant to be called from Java/Kotlin through JNI. +/// It returns an [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute +/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require +/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. /// -/// Parameters: +/// If opening the session fails, an exception is thrown on the JVM, and a null pointer is returned. +/// +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class (parameter required by the JNI interface but unused). -/// - `config`: The path to the Zenoh config file. -/// -/// Returns: -/// - An [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute -/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require -/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. -/// -/// If opening the session fails, a `SessionException` is thrown on the JVM, and a null pointer is returned. +/// - `config_path`: Nullable path to the Zenoh config file. If null, the default configuration will be loaded. /// #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn Java_io_zenoh_jni_JNISession_openSessionViaJNI( mut env: JNIEnv, _class: JClass, - config_path: JString, -) -> *const zenoh::Session { + config_path: /*nullable*/ JString, +) -> *const Session { let session = open_session(&mut env, config_path); match session { Ok(session) => Arc::into_raw(Arc::new(session)), Err(err) => { tracing::error!("Unable to open session: {}", err); - _ = Error::Session(err.to_string()) - .throw_on_jvm(&mut env) - .map_err(|err| { - tracing::error!("Unable to throw exception on session failure: {}", err) - }); + throw_exception!(env, session_error!(err)); null() } } } -/// Open a Zenoh session via JNI. +/// Open a Zenoh session with the configuration pointed out by `config_path`. /// -/// This function is meant to be called from Java/Kotlin through JNI. +/// If the config path provided is null then the default configuration is loaded. /// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class (parameter required by the JNI interface but unused). -/// - `config`: The path to the Zenoh config file. +fn open_session(env: &mut JNIEnv, config_path: JString) -> Result { + let config = if config_path.is_null() { + Config::default() + } else { + let config_file_path = decode_string(env, &config_path)?; + Config::from_file(config_file_path).map_err(|err| session_error!(err))? + }; + zenoh::open(config) + .wait() + .map_err(|err| session_error!(err)) +} + +/// Open a Zenoh session with a JSON configuration. /// -/// Returns: -/// - An [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute -/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require -/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. +/// It returns an [Arc] raw pointer to the Zenoh Session, which should be stored as a private read-only attribute +/// of the session object in the Java/Kotlin code. Subsequent calls to other session functions will require +/// this raw pointer to retrieve the [Session] using `Arc::from_raw`. /// -/// If opening the session fails, a `SessionException` is thrown on the JVM, and a null pointer is returned. +/// If opening the session fails, an exception is thrown on the JVM, and a null pointer is returned. +/// +/// # Parameters: +/// - `env`: The JNI environment. +/// - `_class`: The JNI class (parameter required by the JNI interface but unused). +/// - `json_config`: Nullable configuration as a JSON string. If null, the default configuration will be loaded. /// #[no_mangle] #[allow(non_snake_case)] @@ -95,176 +100,152 @@ pub extern "C" fn Java_io_zenoh_jni_JNISession_openSessionWithJsonConfigViaJNI( mut env: JNIEnv, _class: JClass, json_config: JString, -) -> *const zenoh::Session { +) -> *const Session { let session = open_session_with_json_config(&mut env, json_config); match session { Ok(session) => Arc::into_raw(Arc::new(session)), Err(err) => { tracing::error!("Unable to open session: {}", err); - _ = Error::Session(err.to_string()) - .throw_on_jvm(&mut env) - .map_err(|err| { - tracing::error!("Unable to throw exception on session failure: {}", err) - }); + throw_exception!(env, session_error!(err)); null() } } } -/// Open a Zenoh session. +/// Open a Zenoh session with the provided json configuration. /// -/// Loads the session with the provided by [config_path]. If the config path provided is empty then -/// the default configuration is loaded. +/// If the provided json config is null, then the default config is loaded. /// -/// Returns: -/// - A [Result] with a [zenoh::Session] in case of success or an [Error::Session] in case of failure. -/// -fn open_session(env: &mut JNIEnv, config_path: JString) -> Result { - let config_file_path = decode_string(env, config_path)?; - let config = if config_file_path.is_empty() { +fn open_session_with_json_config(env: &mut JNIEnv, json_config: JString) -> Result { + let config = if json_config.is_null() { Config::default() } else { - Config::from_file(config_file_path).map_err(|err| Error::Session(err.to_string()))? - }; - zenoh::open(config) - .res() - .map_err(|err| Error::Session(err.to_string())) -} - -/// Open a Zenoh session. -/// -/// Loads the session with the provided by [config_path]. If the config path provided is empty then -/// the default configuration is loaded. -/// -/// Returns: -/// - A [Result] with a [zenoh::Session] in case of success or an [Error::Session] in case of failure. -/// -fn open_session_with_json_config(env: &mut JNIEnv, json_config: JString) -> Result { - let json_config = decode_string(env, json_config)?; - let config = if json_config.is_empty() { - Config::default() - } else { - let mut deserializer = match json5::Deserializer::from_str(&json_config) { - Ok(deserializer) => Ok(deserializer), - Err(err) => Err(Error::Session(err.to_string())), - }?; + let json_config = decode_string(env, &json_config)?; + let mut deserializer = + json5::Deserializer::from_str(&json_config).map_err(|err| session_error!(err))?; Config::from_deserializer(&mut deserializer).map_err(|err| match err { - Ok(c) => Error::Session(format!("Invalid configuration: {}", c)), - Err(e) => Error::Session(format!("JSON error: {}", e)), + Ok(c) => session_error!("Invalid configuration: {}", c), + Err(e) => session_error!("JSON error: {}", e), })? }; zenoh::open(config) - .res() - .map_err(|err| Error::Session(err.to_string())) + .wait() + .map_err(|err| session_error!(err)) } /// Closes a Zenoh session via JNI. /// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh session. +/// - `session_ptr`: The raw pointer to the Zenoh session. /// -/// Safety: +/// # Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. /// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The ownership of the session is not transferred, and the session pointer remains valid -/// after this function call, so it is safe to continue using the session. -/// - It is the responsibility of the caller to ensure that the session is not used after it has -/// been freed or dropped. /// - The function may throw a JNI exception in case of failure, which should be handled by the caller. +/// - After the session is closed, the provided pointer is no more valid. /// #[no_mangle] #[allow(non_snake_case, unused)] pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_closeSessionViaJNI( mut env: JNIEnv, _class: JClass, - ptr: *const zenoh::Session, + session_ptr: *const Session, ) { - let ptr = Arc::try_unwrap(Arc::from_raw(ptr)); + let ptr = Arc::try_unwrap(Arc::from_raw(session_ptr)); match ptr { Ok(session) => { // Do nothing, the pointer will be freed. } Err(arc_session) => { let ref_count = Arc::strong_count(&arc_session); - tracing::error!("Unable to close the session."); - _ = Error::Session(format!( + throw_exception!(env, session_error!( "Attempted to close the session, but at least one strong reference to it is still alive (ref count: {}). All the declared publishers, subscribers, and queryables need to be dropped first.", ref_count - )) - .throw_on_jvm(&mut env) - .map_err(|err| tracing::error!("Unable to throw exception on session failure: {}", err)); + )); } }; } /// Declare a Zenoh publisher via JNI. /// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `key_expr`: Raw pointer of the [KeyExpr] to be used for the publisher. -/// - `session_ptr`: The raw pointer to the Zenoh [Session] from which to declare the publisher. -/// - `congestion_control`: The [CongestionControl] mechanism specified as an ordinal. -/// - `priority`: The [Priority] mechanism specified as an ordinal. -/// -/// Returns: +/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the publisher, may be null. +/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the publisher. +/// It is only considered when the key_expr_ptr parameter is null, meaning the function is +/// receiving a key expression that was not declared. +/// - `session_ptr`: Raw pointer to the Zenoh [Session] to be used for the publisher. +/// - `congestion_control`: The [zenoh::publisher::CongestionControl] configuration as an ordinal. +/// - `priority`: The [zenoh::core::Priority] configuration as an ordinal. +/// - `is_express`: The express config of the publisher (see [zenoh::prelude::QoSBuilderTrait]). +/// +/// # Returns: /// - A raw pointer to the declared Zenoh publisher or null in case of failure. /// -/// Safety: +/// # Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. /// - It assumes that the provided session pointer is valid and has not been modified or freed. /// - The ownership of the session is not transferred, and the session pointer remains valid /// after this function call so it is safe to use it after this call. -/// - The returned raw pointer should be stored appropriately and later freed using `Java_io_zenoh_jni_JNIPublisher_freePtrViaJNI`. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. +/// - The function may throw an exception in case of failure, which should be handled by the caller. /// #[no_mangle] #[allow(non_snake_case)] pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declarePublisherViaJNI( mut env: JNIEnv, _class: JClass, - key_expr_ptr: *const KeyExpr<'static>, - session_ptr: *const zenoh::Session, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, + session_ptr: *const Session, congestion_control: jint, priority: jint, -) -> *const zenoh::publication::Publisher<'static> { - let result = declare_publisher(key_expr_ptr, session_ptr, congestion_control, priority); - match result { - Ok(ptr) => ptr, - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on publisher declaration failure. {}", - err - ) - }); - null() + is_express: jboolean, +) -> *const Publisher<'static> { + let session = Arc::from_raw(session_ptr); + let publisher_ptr = || -> Result<*const Publisher<'static>> { + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let congestion_control = decode_congestion_control(congestion_control)?; + let priority = decode_priority(priority)?; + let result = session + .declare_publisher(key_expr) + .congestion_control(congestion_control) + .priority(priority) + .express(is_express != 0) + .wait(); + match result { + Ok(publisher) => Ok(Arc::into_raw(Arc::new(publisher))), + Err(err) => Err(session_error!(err)), } - } + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + null() + }); + std::mem::forget(session); + publisher_ptr } /// Performs a `put` operation in the Zenoh session via JNI. /// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// /// Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the operation. +/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the operation, may be null. +/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the operation. +/// It is only considered when the key_expr_ptr parameter is null, meaning the function is +/// receiving a key expression that was not declared. /// - `session_ptr`: Raw pointer to the [Session] to be used for the operation. /// - `payload`: The payload to send through the network. -/// - `encoding`: The [Encoding] of the put operation. +/// - `encoding_id`: The encoding id of the payload. +/// - `encoding_schema`: Optional encoding schema, may be null. /// - `congestion_control`: The [CongestionControl] mechanism specified. /// - `priority`: The [Priority] mechanism specified. -/// - `sample_kind`: The [SampleKind] of the put operation. +/// - `is_express`: The express flag. /// - `attachment`: Optional attachment encoded into a byte array. May be null. /// /// Safety: @@ -272,65 +253,131 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declarePublisherViaJNI( /// - It assumes that the provided session pointer is valid and has not been modified or freed. /// - The session pointer remains valid and the ownership of the session is not transferred, /// allowing safe usage of the session after this function call. -/// - The function may throw a JNI exception or a Session exception in case of failure, which -/// should be handled by the Java/Kotlin caller. +/// - The function may throw an exception in case of failure, which should be handled by the Java/Kotlin caller. /// #[no_mangle] #[allow(non_snake_case)] pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_putViaJNI( mut env: JNIEnv, _class: JClass, - key_expr_ptr: *const KeyExpr<'static>, - session_ptr: *const zenoh::Session, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, + session_ptr: *const Session, payload: JByteArray, - encoding: jint, + encoding_id: jint, + encoding_schema: JString, congestion_control: jint, priority: jint, - sample_kind: jint, + is_express: jboolean, attachment: JByteArray, ) { let session = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr_ptr); - match on_put( - &mut env, - &key_expr, - &session, - payload, - encoding, - congestion_control, - priority, - sample_kind, - attachment, - ) { - Ok(_) => {} - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on query declaration failure. {}", - err - ) - }); + let _ = || -> Result<()> { + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let payload = decode_byte_array(&env, payload)?; + let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; + let congestion_control = decode_congestion_control(congestion_control)?; + let priority = decode_priority(priority)?; + + let mut put_builder = session + .put(&key_expr, payload) + .congestion_control(congestion_control) + .encoding(encoding) + .express(is_express != 0) + .priority(priority); + + if !attachment.is_null() { + let attachment = decode_byte_array(&env, attachment)?; + put_builder = put_builder.attachment(attachment) } - } + + put_builder + .wait() + .map(|_| tracing::trace!("Put on '{key_expr}'")) + .map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); std::mem::forget(session); - std::mem::forget(key_expr); } -/// Declare a Zenoh subscriber via JNI. +/// Performs a `delete` operation in the Zenoh session via JNI. /// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// Parameters: +/// - `env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `key_expr_ptr`: Raw pointer to the [KeyExpr] to be used for the operation, may be null. +/// - `key_expr_str`: String representation of the [KeyExpr] to be used for the operation. +/// It is only considered when the key_expr_ptr parameter is null, meaning the function is +/// receiving a key expression that was not declared. +/// - `session_ptr`: Raw pointer to the [Session] to be used for the operation. +/// - `congestion_control`: The [CongestionControl] mechanism specified. +/// - `priority`: The [Priority] mechanism specified. +/// - `is_express`: The express flag. +/// - `attachment`: Optional attachment encoded into a byte array. May be null. +/// +/// Safety: +/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. +/// - It assumes that the provided session pointer is valid and has not been modified or freed. +/// - The session pointer remains valid and the ownership of the session is not transferred, +/// allowing safe usage of the session after this function call. +/// - The function may throw a JNI exception or a Session exception in case of failure, which +/// should be handled by the Java/Kotlin caller. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_deleteViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, + session_ptr: *const Session, + congestion_control: jint, + priority: jint, + is_express: jboolean, + attachment: JByteArray, +) { + let session = Arc::from_raw(session_ptr); + let _ = || -> Result<()> { + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let congestion_control = decode_congestion_control(congestion_control)?; + let priority = decode_priority(priority)?; + + let mut delete_builder = session + .delete(&key_expr) + .congestion_control(congestion_control) + .express(is_express != 0) + .priority(priority); + + if !attachment.is_null() { + let attachment = decode_byte_array(&env, attachment)?; + delete_builder = delete_builder.attachment(attachment) + } + + delete_builder + .wait() + .map(|_| tracing::trace!("Delete on '{key_expr}'")) + .map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); + std::mem::forget(session); +} + +/// Declare a Zenoh subscriber via JNI. /// /// Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `key_expr`: The key expression for the subscriber. -/// - `ptr`: The raw pointer to the Zenoh session. +/// - `key_expr_ptr`: The key expression pointer for the subscriber. May be null in case of using an +/// undeclared key expression. +/// - `key_expr_str`: String representation of the key expression to be used to declare the subscriber. +/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. +/// - `session_ptr`: The raw pointer to the Zenoh session. /// - `callback`: The callback function as an instance of the `JNISubscriberCallback` interface in Java/Kotlin. /// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the subscriber. -/// - `reliability`: The [Reliability] value as an ordinal. +/// - `reliability`: The reliability value as an ordinal. /// /// Returns: -/// - A raw pointer to the declared Zenoh subscriber or null in case of failure. +/// - A raw pointer to the declared Zenoh subscriber. In case of failure, an exception is thrown and null is returned. /// /// Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. @@ -346,24 +393,105 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_putViaJNI( pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareSubscriberViaJNI( mut env: JNIEnv, _class: JClass, - key_expr_ptr: *const KeyExpr<'static>, - ptr: *const zenoh::Session, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, + session_ptr: *const Session, callback: JObject, on_close: JObject, reliability: jint, -) -> *const zenoh::subscriber::Subscriber<'static, ()> { - match declare_subscriber(&mut env, key_expr_ptr, ptr, callback, on_close, reliability) { - Ok(subscriber_ptr) => subscriber_ptr, - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on subscriber declaration failure: {}", - err - ) - }); - null() - } - } +) -> *const Subscriber<'static, ()> { + let session = Arc::from_raw(session_ptr); + || -> Result<*const Subscriber<'static, ()>> { + let java_vm = Arc::new(get_java_vm(&mut env)?); + let callback_global_ref = get_callback_global_ref(&mut env, callback)?; + let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; + let reliability = decode_reliability(reliability)?; + let on_close = load_on_close(&java_vm, on_close_global_ref); + + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + tracing::debug!("Declaring subscriber on '{}'...", key_expr); + + let result = session + .declare_subscriber(key_expr.to_owned()) + .callback(move |sample| { + on_close.noop(); // Moves `on_close` inside the closure so it gets destroyed with the closure + let _ = || -> Result<()> { + let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { + jni_error!("Unable to attach thread for subscriber: {}", err) + })?; + let byte_array = bytes_to_java_array(&env, sample.payload()) + .map(|array| env.auto_local(array))?; + + let encoding_id: jint = sample.encoding().id() as jint; + let encoding_schema = match sample.encoding().schema() { + Some(schema) => slice_to_java_string(&env, schema)?, + None => JString::default(), + }; + let kind = sample.kind() as jint; + let (timestamp, is_valid) = sample + .timestamp() + .map(|timestamp| (timestamp.get_time().as_u64(), true)) + .unwrap_or((0, false)); + + let attachment_bytes = sample + .attachment() + .map_or_else( + || Ok(JByteArray::default()), + |attachment| bytes_to_java_array(&env, attachment), + ) + .map(|array| env.auto_local(array)) + .map_err(|err| jni_error!("Error processing attachment: {}", err))?; + + let key_expr_str = + env.auto_local(env.new_string(sample.key_expr().to_string()).map_err( + |err| jni_error!("Error processing sample key expr: {}", err), + )?); + + let express = sample.express(); + let priority = sample.priority() as jint; + let cc = sample.congestion_control() as jint; + + env.call_method( + &callback_global_ref, + "run", + "(Ljava/lang/String;[BILjava/lang/String;IJZ[BZII)V", + &[ + JValue::from(&key_expr_str), + JValue::from(&byte_array), + JValue::from(encoding_id), + JValue::from(&encoding_schema), + JValue::from(kind), + JValue::from(timestamp as i64), + JValue::from(is_valid), + JValue::from(&attachment_bytes), + JValue::from(express), + JValue::from(priority), + JValue::from(cc), + ], + ) + .map_err(|err| jni_error!(err))?; + Ok(()) + }() + .map_err(|err| tracing::error!("On subscriber callback error: {err}")); + }) + .reliability(reliability) + .wait(); + + let subscriber = + result.map_err(|err| session_error!("Unable to declare subscriber: {}", err))?; + + tracing::debug!( + "Subscriber declared on '{}' with reliability '{:?}'.", + key_expr, + reliability + ); + std::mem::forget(session); + Ok(Arc::into_raw(Arc::new(subscriber))) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + null() + }) } /// Declare a Zenoh queryable via JNI. @@ -373,14 +501,17 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareSubscriberViaJNI( /// Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to be used for the queryable. +/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to be used for the queryable. May be null in case of using an +/// undeclared key expression. +/// - `key_expr_str`: String representation of the key expression to be used to declare the queryable. +/// It won't be considered in case a key_expr_ptr to a declared key expression is provided. /// - `session_ptr`: A raw pointer to the Zenoh [Session] to be used to declare the queryable. /// - `callback`: The callback function as an instance of the `JNIQueryableCallback` interface in Java/Kotlin. /// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called upon closing the queryable. /// - `complete`: The completeness of the queryable. /// /// Returns: -/// - A raw pointer to the declared Zenoh queryable or null in case of failure. +/// - A raw pointer to the declared Zenoh queryable. In case of failure, an exception is thrown and null is returned. /// /// Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. @@ -396,47 +527,152 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareSubscriberViaJNI( pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareQueryableViaJNI( mut env: JNIEnv, _class: JClass, - key_expr_ptr: *const KeyExpr<'static>, - session_ptr: *const zenoh::Session, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, + session_ptr: *const Session, callback: JObject, on_close: JObject, complete: jboolean, -) -> *const zenoh::queryable::Queryable<'static, ()> { - match declare_queryable( - &mut env, - key_expr_ptr, - session_ptr, - callback, - on_close, - complete, - ) { - Ok(queryable) => Arc::into_raw(Arc::new(queryable)), - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on query declaration failure. {}", - err - ) - }); - null() - } - } +) -> *const Queryable<'static, ()> { + let session = Arc::from_raw(session_ptr); + let query_ptr = || -> Result<*const Queryable<'static, ()>> { + let java_vm = Arc::new(get_java_vm(&mut env)?); + let callback_global_ref = get_callback_global_ref(&mut env, callback)?; + let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let complete = complete != 0; + let on_close = load_on_close(&java_vm, on_close_global_ref); + tracing::debug!("Declaring queryable through JNI on {}", key_expr); + let builder = session + .declare_queryable(key_expr) + .callback(move |query| { + on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure + let env = match java_vm.attach_current_thread_as_daemon() { + Ok(env) => env, + Err(err) => { + tracing::error!("Unable to attach thread for queryable callback: {}", err); + return; + } + }; + + tracing::debug!("Receiving query through JNI: {}", query.to_string()); + match on_query(env, query, &callback_global_ref) { + Ok(_) => tracing::debug!("Queryable callback called successfully."), + Err(err) => tracing::error!("Error calling queryable callback: {}", err), + } + }) + .complete(complete); + + let queryable = builder + .wait() + .map_err(|err| session_error!("Error declaring queryable: {}", err))?; + Ok(Arc::into_raw(Arc::new(queryable))) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + null() + }); + std::mem::forget(session); + query_ptr +} + +fn on_query(mut env: JNIEnv, query: Query, callback_global_ref: &GlobalRef) -> Result<()> { + let selector_params_jstr = env + .new_string(query.parameters().to_string()) + .map(|value| env.auto_local(value)) + .map_err(|err| { + jni_error!( + "Could not create a JString through JNI for the Query key expression. {}", + err + ) + })?; + + let (with_value, payload, encoding_id, encoding_schema) = if let Some(payload) = query.payload() + { + let encoding = query.encoding().unwrap(); //If there is payload, there is encoding. + let encoding_id = encoding.id() as jint; + let encoding_schema = encoding + .schema() + .map_or_else( + || Ok(JString::default()), + |schema| slice_to_java_string(&env, schema), + ) + .map(|value| env.auto_local(value))?; + let byte_array = bytes_to_java_array(&env, payload).map(|value| env.auto_local(value))?; + (true, byte_array, encoding_id, encoding_schema) + } else { + ( + false, + env.auto_local(JByteArray::default()), + 0, + env.auto_local(JString::default()), + ) + }; + + let attachment_bytes = query + .attachment() + .map_or_else( + || Ok(JByteArray::default()), + |attachment| bytes_to_java_array(&env, attachment), + ) + .map(|value| env.auto_local(value)) + .map_err(|err| jni_error!("Error processing attachment of reply: {}.", err))?; + + let key_expr_str = env + .new_string(&query.key_expr().to_string()) + .map(|key_expr| env.auto_local(key_expr)) + .map_err(|err| { + jni_error!( + "Could not create a JString through JNI for the Query key expression: {}.", + err + ) + })?; + + let query_ptr = Arc::into_raw(Arc::new(query)); + + let result = env + .call_method( + callback_global_ref, + "run", + "(Ljava/lang/String;Ljava/lang/String;Z[BILjava/lang/String;[BJ)V", + &[ + JValue::from(&key_expr_str), + JValue::from(&selector_params_jstr), + JValue::from(with_value), + JValue::from(&payload), + JValue::from(encoding_id), + JValue::from(&encoding_schema), + JValue::from(&attachment_bytes), + JValue::from(query_ptr as jlong), + ], + ) + .map(|_| ()) + .map_err(|err| { + // The callback could not be invoked, therefore the created kotlin query object won't be + // used. Since `query_ptr` as well as `key_expr_ptr` was created within this function + // and remains unaltered, it is safe to reclaim ownership of the memory by converting + // the raw pointers back into an `Arc` and freeing the memory. + unsafe { + Arc::from_raw(query_ptr); + }; + _ = env.exception_describe(); + jni_error!(err) + }); + result } /// Declare a [KeyExpr] through a [Session] via JNI. /// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. /// - `session_ptr`: A raw pointer to the Zenoh [Session] from which to declare the key expression. /// - `key_expr_str`: A Java String with the intended key expression. /// -/// Returns: -/// - A raw pointer to the declared key expression or null in case of failure. +/// # Returns: +/// - A raw pointer to the declared key expression. In case of failure, an exception is thrown and null is returned. /// -/// Safety: +/// # Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. /// - It assumes that the provided session pointer is valid and has not been modified or freed. /// - The session pointer remains valid and the ownership of the session is not transferred, @@ -448,41 +684,50 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareQueryableViaJNI( pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareKeyExprViaJNI( mut env: JNIEnv, _class: JClass, - session_ptr: *const zenoh::Session, + session_ptr: *const Session, key_expr_str: JString, ) -> *const KeyExpr<'static> { - match declare_keyexpr(&mut env, session_ptr, key_expr_str) { - Ok(key_expr) => Arc::into_raw(Arc::new(key_expr)), - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on key expr declaration failure. {}", + let session: Arc = Arc::from_raw(session_ptr); + let key_expr_ptr = || -> Result<*const KeyExpr<'static>> { + let key_expr_str = decode_string(&mut env, &key_expr_str)?; + let key_expr = session + .declare_keyexpr(key_expr_str.to_owned()) + .wait() + .map_err(|err| { + session_error!( + "Unable to declare key expression '{}': {}", + key_expr_str, err ) - }); - null() - } - } + })?; + Ok(Arc::into_raw(Arc::new(key_expr))) + }() + .unwrap_or_else(|err| { + throw_exception!(env, err); + null() + }); + mem::forget(session); + key_expr_ptr } /// Undeclare a [KeyExpr] through a [Session] via JNI. /// /// The key expression must have been previously declared on the specified session, otherwise an -/// error is thrown and propagated to the caller. +/// exception is thrown. /// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// This functions frees the key expression pointer provided. /// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. /// - `session_ptr`: A raw pointer to the Zenoh [Session] from which to undeclare the key expression. /// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to undeclare. /// -/// Safety: +/// # Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. /// - It assumes that the provided session and keyexpr pointers are valid and have not been modified or freed. -/// - Both session pointer and key expression pointers will remain valid. -/// Their ownership is not transferred, allowing safe usage of the session and the key expression after this function call. +/// - The session pointer remains valid after this function call. +/// - The key expression pointer is voided after this function call. /// - The function may throw an exception in case of failure, which should be handled by the caller. /// #[no_mangle] @@ -490,116 +735,47 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_declareKeyExprViaJNI( pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_undeclareKeyExprViaJNI( mut env: JNIEnv, _class: JClass, - session_ptr: *const zenoh::Session, + session_ptr: *const Session, key_expr_ptr: *const KeyExpr<'static>, ) { let session = Arc::from_raw(session_ptr); let key_expr = Arc::from_raw(key_expr_ptr); let key_expr_clone = key_expr.deref().clone(); - match session.undeclare(key_expr_clone).res() { + match session.undeclare(key_expr_clone).wait() { Ok(_) => {} Err(err) => { - _ = Error::Session(format!( - "Unable to declare key expression {key_expr}: {}", - err - )) - .throw_on_jvm(&mut env) + throw_exception!( + env, + session_error!("Unable to declare key expression '{}': {}", key_expr, err) + ); } } std::mem::forget(session); - std::mem::forget(key_expr); -} - -/// Performs a `get` operation in the Zenoh session via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: -/// - `env`: The JNI environment. -/// - `_class`: The JNI class. -/// - `key_expr`: Pointer to the key expression for the `get`. -/// - `selector_params`: Parameters of the selector. -/// - `session_ptr`: A raw pointer to the Zenoh session. -/// - `callback`: An instance of the Java/Kotlin `JNIGetCallback` function interface to be called upon receiving a reply. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called when the get operation won't receive -/// any more replies. -/// - `timeout_ms`: The timeout in milliseconds. -/// - `target`: The [QueryTarget] as the ordinal of the enum. -/// - `consolidation`: The [ConsolidationMode] as the ordinal of the enum. -/// - `attachment`: An optional attachment encoded into a byte array. -/// -/// Safety: -/// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. -/// - It assumes that the provided session pointer is valid and has not been modified or freed. -/// - The session pointer remains valid and the ownership of the session is not transferred, -/// allowing safe usage of the session after this function call. -/// - The function may throw a JNI exception in case of failure, which should be handled by the caller. -/// -/// Throws: -/// - An exception in case of failure handling the query. -/// -#[no_mangle] -#[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getViaJNI( - mut env: JNIEnv, - _class: JClass, - key_expr: *const KeyExpr<'static>, - selector_params: JString, - session_ptr: *const zenoh::Session, - callback: JObject, - on_close: JObject, - timeout_ms: jlong, - target: jint, - consolidation: jint, - attachment: JByteArray, -) { - let session = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr); - match on_get_query( - &mut env, - &key_expr, - selector_params, - &session, - callback, - on_close, - timeout_ms, - target, - consolidation, - None, - attachment, - ) { - Ok(_) => {} - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on get operation failure. {}", - err - ) - }); - } - } - std::mem::forget(session); - std::mem::forget(key_expr); + // `key_expr` is intentionally left to be freed by Rust } /// Performs a `get` operation in the Zenoh session via JNI with Value. /// -/// This function is meant to be called from Java/Kotlin code through JNI. -/// -/// Parameters: +/// # Parameters: /// - `env`: The JNI environment. /// - `_class`: The JNI class. -/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to be used for the operation. +/// - `key_expr_ptr`: Raw pointer to a declared [KeyExpr] to be used for the query. May be null in case +/// of using a non declared key expression, in which case the `key_expr_str` parameter will be used instead. +/// - `key_expr_str`: String representation of the key expression to be used to declare the query. It is not +/// considered if a `key_expr_ptr` is provided. /// - `selector_params`: Parameters of the selector. /// - `session_ptr`: A raw pointer to the Zenoh [Session]. /// - `callback`: A Java/Kotlin callback to be called upon receiving a reply. /// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called when no more replies will be received. /// - `timeout_ms`: The timeout in milliseconds. -/// - `target`: The [QueryTarget] as the ordinal of the enum. -/// - `consolidation`: The [ConsolidationMode] as the ordinal of the enum. -/// - `payload`: The payload of the [Value] -/// - `encoding`: The [Encoding] as the ordinal of the enum. +/// - `target`: The query target as the ordinal of the enum. +/// - `consolidation`: The consolidation mode as the ordinal of the enum. /// - `attachment`: An optional attachment encoded into a byte array. +/// - `with_value`: Boolean value to tell if a value must be included in the get operation. If true, +/// then the next params are valid. +/// - `payload`: The payload of the value. +/// - `encoding_id`: The encoding of the value payload. +/// - `encoding_schema`: The encoding schema of the value payload, may be null. /// /// Safety: /// - The function is marked as unsafe due to raw pointer manipulation and JNI interaction. @@ -613,154 +789,226 @@ pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getViaJNI( /// #[no_mangle] #[allow(non_snake_case)] -pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getWithValueViaJNI( +pub unsafe extern "C" fn Java_io_zenoh_jni_JNISession_getViaJNI( mut env: JNIEnv, _class: JClass, - key_expr_ptr: *const KeyExpr<'static>, + key_expr_ptr: /*nullable*/ *const KeyExpr<'static>, + key_expr_str: JString, selector_params: JString, - session_ptr: *const zenoh::Session, + session_ptr: *const Session, callback: JObject, on_close: JObject, timeout_ms: jlong, target: jint, consolidation: jint, - payload: JByteArray, - encoding: jint, - attachment: JByteArray, + attachment: /*nullable*/ JByteArray, + with_value: jboolean, + payload: /*nullable*/ JByteArray, + encoding_id: jint, + encoding_schema: /*nullable*/ JString, ) { let session = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr_ptr); - match on_get_query( - &mut env, - &key_expr, - selector_params, - &session, - callback, - on_close, - timeout_ms, - target, - consolidation, - Some((payload, encoding)), - attachment, - ) { - Ok(_) => {} - Err(err) => { - _ = err.throw_on_jvm(&mut env).map_err(|err| { - tracing::error!( - "Unable to throw exception on get operation failure. {}", - err - ) - }); + let _ = || -> Result<()> { + let key_expr = process_kotlin_key_expr(&mut env, &key_expr_str, key_expr_ptr)?; + let java_vm = Arc::new(get_java_vm(&mut env)?); + let callback_global_ref = get_callback_global_ref(&mut env, callback)?; + let on_close_global_ref = get_callback_global_ref(&mut env, on_close)?; + let query_target = decode_query_target(target)?; + let consolidation = decode_consolidation(consolidation)?; + let selector_params = decode_string(&mut env, &selector_params)?; + let timeout = Duration::from_millis(timeout_ms as u64); + let on_close = load_on_close(&java_vm, on_close_global_ref); + let selector = Selector::owned(&key_expr, &*selector_params); + let mut get_builder = session + .get(selector) + .callback(move |reply| { + || -> Result<()> { + on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure + tracing::debug!("Receiving reply through JNI: {:?}", reply); + let mut env = java_vm.attach_current_thread_as_daemon().map_err(|err| { + jni_error!("Unable to attach thread for GET query callback: {}.", err) + })?; + + match reply.result() { + Ok(sample) => on_reply_success( + &mut env, + reply.replier_id(), + sample, + &callback_global_ref, + ), + Err(error) => on_reply_error( + &mut env, + reply.replier_id(), + error, + &callback_global_ref, + ), + } + }() + .unwrap_or_else(|err| tracing::error!("Error on get callback: {err}")); + }) + .target(query_target) + .timeout(timeout) + .consolidation(consolidation); + + if with_value != 0 { + let encoding = decode_encoding(&mut env, encoding_id, &encoding_schema)?; + get_builder = get_builder.encoding(encoding); + get_builder = get_builder.payload(decode_byte_array(&env, payload)?); } - } + + if !attachment.is_null() { + let attachment = decode_byte_array(&env, attachment)?; + get_builder = get_builder.attachment::>(attachment); + } + + get_builder + .wait() + .map(|_| tracing::trace!("Performing get on '{key_expr}'.",)) + .map_err(|err| session_error!(err)) + }() + .map_err(|err| throw_exception!(env, err)); std::mem::forget(session); - std::mem::forget(key_expr); } -/// Performs a `get` operation in the Zenoh session via JNI. -/// -/// Parameters: -/// - `env`: A mutable reference to the JNI environment. -/// - `key_expr`: The key expression for the `get` operation. -/// - `session`: An `Arc` representing the Zenoh session. -/// - `callback`: A Java/Kotlin `JNIGetCallback` function interface to be called upon receiving a reply. -/// - `on_close`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called when Zenoh notifies -/// that no more replies will be received. -/// - `timeout_ms`: The timeout in milliseconds. -/// - `target`: The [QueryTarget] as the ordinal of the enum. -/// - `consolidation`: The [ConsolidationMode] as the ordinal of the enum. -/// - `value_params`: Parameters of the value (payload as [JByteArray] and encoding as [jint]) to -/// be set in case the get is performed "with value". -/// - `encoded_attachment`: An optional attachment encoded into a byte array. -/// -/// Returns: -/// - A `Result` indicating the result of the `get` operation. -/// -#[allow(clippy::too_many_arguments)] -fn on_get_query( +fn on_reply_success( env: &mut JNIEnv, - key_expr: &Arc>, - selector_params: JString, - session: &Arc, - callback: JObject, - on_close: JObject, - timeout_ms: jlong, - target: jint, - consolidation: jint, - value_params: Option<(JByteArray, jint)>, - encoded_attachment: JByteArray, + replier_id: Option, + sample: &Sample, + callback_global_ref: &GlobalRef, ) -> Result<()> { - let java_vm = Arc::new(get_java_vm(env)?); - let callback_global_ref = get_callback_global_ref(env, callback)?; - let on_close_global_ref = get_callback_global_ref(env, on_close)?; - let query_target = decode_query_target(target)?; - let consolidation = decode_consolidation(consolidation)?; - let selector_params = decode_string(env, selector_params)?; - let timeout = Duration::from_millis(timeout_ms as u64); - let on_close = load_on_close(&java_vm, on_close_global_ref); + let zenoh_id = replier_id + .map_or_else( + || Ok(JString::default()), + |replier_id| { + env.new_string(replier_id.to_string()) + .map_err(|err| jni_error!(err)) + }, + ) + .map(|value| env.auto_local(value))?; - let key_expr_clone = key_expr.deref().clone(); - let selector = Selector::from(key_expr_clone).with_parameters(&selector_params); - let mut get_builder = session - .get(selector) - .callback(move |reply| { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - tracing::debug!("Receiving reply through JNI: {:?}", reply); - let env = match java_vm.attach_current_thread_as_daemon() { - Ok(env) => env, - Err(err) => { - tracing::error!("Unable to attach thread for GET query callback: {}", err); - return; - } - }; - match on_reply(env, reply, &callback_global_ref) { - Ok(_) => {} - Err(err) => tracing::error!("{}", err), - } - }) - .target(query_target) - .timeout(timeout) - .consolidation(consolidation); - - let mut binding = None; - if let Some((payload, encoding)) = value_params { - let value = decode_value(env, payload, encoding)?; - get_builder = get_builder.with_value(value.to_owned()); - binding = Some(value) - } + let byte_array = + bytes_to_java_array(env, sample.payload()).map(|value| env.auto_local(value))?; + let encoding: jint = sample.encoding().id() as jint; + let encoding_schema = sample + .encoding() + .schema() + .map_or_else( + || Ok(JString::default()), + |schema| slice_to_java_string(env, schema), + ) + .map(|value| env.auto_local(value))?; + let kind = sample.kind() as jint; - if !encoded_attachment.is_null() { - let aux = decode_byte_array(env, encoded_attachment)?; - let attachment = vec_to_attachment(aux); - get_builder = get_builder.with_attachment(attachment); - } + let (timestamp, is_valid) = sample + .timestamp() + .map(|timestamp| (timestamp.get_time().as_u64(), true)) + .unwrap_or((0, false)); - get_builder - .res() - .map(|_| { - tracing::trace!( - "Performing get on {key_expr:?}, with target '{query_target:?}', with timeout '{timeout:?}', consolidation '{consolidation:?}', with value: '{binding:?}'", + let attachment_bytes = sample + .attachment() + .map_or_else( + || Ok(JByteArray::default()), + |attachment| bytes_to_java_array(env, attachment), + ) + .map(|value| env.auto_local(value)) + .map_err(|err| jni_error!("Error processing attachment of reply: {}.", err))?; + + let key_expr_str = env + .new_string(sample.key_expr().to_string()) + .map(|value| env.auto_local(value)) + .map_err(|err| { + jni_error!( + "Could not create a JString through JNI for the Sample key expression. {}", + err ) - }) - .map_err(|err| Error::Session(err.to_string()))?; - Ok(()) + })?; + + let express = sample.express(); + let priority = sample.priority() as jint; + let cc = sample.congestion_control() as jint; + + let result = match env.call_method( + callback_global_ref, + "run", + "(Ljava/lang/String;ZLjava/lang/String;[BILjava/lang/String;IJZ[BZII)V", + &[ + JValue::from(&zenoh_id), + JValue::from(true), + JValue::from(&key_expr_str), + JValue::from(&byte_array), + JValue::from(encoding), + JValue::from(&encoding_schema), + JValue::from(kind), + JValue::from(timestamp as i64), + JValue::from(is_valid), + JValue::from(&attachment_bytes), + JValue::from(express), + JValue::from(priority), + JValue::from(cc), + ], + ) { + Ok(_) => Ok(()), + Err(err) => { + _ = env.exception_describe(); + Err(jni_error!("On GET callback error: {}", err)) + } + }; + result } -pub(crate) unsafe fn declare_keyexpr( +fn on_reply_error( env: &mut JNIEnv, - session_ptr: *const Session, - key_expr: JString, -) -> Result> { - let key_expr = decode_string(env, key_expr)?; - let session: Arc = Arc::from_raw(session_ptr); - let result = session.declare_keyexpr(key_expr.to_owned()).res(); - std::mem::forget(session); + replier_id: Option, + reply_error: &ReplyError, + callback_global_ref: &GlobalRef, +) -> Result<()> { + let zenoh_id = replier_id + .map_or_else( + || Ok(JString::default()), + |replier_id| { + env.new_string(replier_id.to_string()) + .map_err(|err| jni_error!(err)) + }, + ) + .map(|value| env.auto_local(value))?; - match result { - Ok(key_expr) => Ok(key_expr), - Err(err) => Err(Error::Session(format!( - "Unable to declare key expression {key_expr}: {}", - err - ))), - } + let payload = + bytes_to_java_array(env, reply_error.payload()).map(|value| env.auto_local(value))?; + let encoding_id: jint = reply_error.encoding().id() as jint; + let encoding_schema = reply_error + .encoding() + .schema() + .map_or_else( + || Ok(JString::default()), + |schema| slice_to_java_string(env, schema), + ) + .map(|value| env.auto_local(value))?; + let result = match env.call_method( + callback_global_ref, + "run", + "(Ljava/lang/String;ZLjava/lang/String;[BILjava/lang/String;IJZ[BZII)V", + &[ + JValue::from(&zenoh_id), + JValue::from(false), + JValue::from(&JString::default()), + JValue::from(&payload), + JValue::from(encoding_id), + JValue::from(&encoding_schema), + // The remaining parameters aren't used in case of replying error, so we set them to default. + JValue::from(0 as jint), + JValue::from(0_i64), + JValue::from(false), + JValue::from(&JByteArray::default()), + JValue::from(false), + JValue::from(0 as jint), + JValue::from(0 as jint), + ], + ) { + Ok(_) => Ok(()), + Err(err) => { + _ = env.exception_describe(); + Err(jni_error!("On GET callback error: {}", err)) + } + }; + result } diff --git a/zenoh-jni/src/subscriber.rs b/zenoh-jni/src/subscriber.rs index 169e554..3462ec7 100644 --- a/zenoh-jni/src/subscriber.rs +++ b/zenoh-jni/src/subscriber.rs @@ -12,32 +12,19 @@ // ZettaScale Zenoh Team, // -use std::{ops::Deref, sync::Arc}; +use std::sync::Arc; -use jni::{ - objects::{JClass, JObject, JValue}, - sys::{jint, jlong}, - JNIEnv, -}; -use zenoh::prelude::r#sync::*; -use zenoh::subscriber::Subscriber; +use jni::{objects::JClass, JNIEnv}; +use zenoh::pubsub::Subscriber; -use crate::utils::{get_callback_global_ref, get_java_vm, load_on_close}; -use crate::{ - errors::{Error, Result}, - utils::attachment_to_vec, -}; - -/// Frees the memory associated with a Zenoh subscriber raw pointer via JNI. -/// -/// This function is meant to be called from Java/Kotlin code through JNI. +/// Frees the [Subscriber]. /// -/// Parameters: +/// # Parameters: /// - `_env`: The JNI environment. /// - `_class`: The JNI class. -/// - `ptr`: The raw pointer to the Zenoh subscriber ([Subscriber]). +/// - `subscriber_ptr`: The raw pointer to the Zenoh subscriber ([Subscriber]). /// -/// Safety: +/// # Safety: /// - The function is marked as unsafe due to raw pointer manipulation. /// - It assumes that the provided subscriber pointer is valid and has not been modified or freed. /// - The function takes ownership of the raw pointer and releases the associated memory. @@ -48,138 +35,7 @@ use crate::{ pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNISubscriber_freePtrViaJNI( _env: JNIEnv, _: JClass, - ptr: *const zenoh::subscriber::Subscriber<()>, + subscriber_ptr: *const Subscriber<()>, ) { - Arc::from_raw(ptr); -} - -/// Declares a Zenoh subscriber via JNI. -/// -/// Parameters: -/// - `env`: A mutable reference to the JNI environment. -/// - `key_expr_ptr`: Raw pointer to the key expression to be used for the subscriber. -/// - `session_ptr`: Raw pointer to the session to be used for the declaration.. -/// - `callback`: The callback function as an instance of the `Callback` interface in Java/Kotlin. -/// - `onClose`: A Java/Kotlin `JNIOnCloseCallback` function interface to be called when the subscriber is undeclared. -/// - `reliability`: The [Reliability] configuration as an ordinal. -/// -/// Returns: -/// - A [Result] containing a raw pointer to the declared Zenoh subscriber ([Subscriber]) in case of success, -/// or an [Error] variant in case of failure. -/// -/// Safety: -/// - The returned raw pointer should be stored appropriately and later freed using [Java_io_zenoh_jni_JNISubscriber_freePtrViaJNI]. -/// -pub(crate) unsafe fn declare_subscriber( - env: &mut JNIEnv, - key_expr_ptr: *const KeyExpr<'static>, - session_ptr: *const zenoh::Session, - callback: JObject, - on_close: JObject, - reliability: jint, -) -> Result<*const Subscriber<'static, ()>> { - let java_vm = Arc::new(get_java_vm(env)?); - let callback_global_ref = get_callback_global_ref(env, callback)?; - let on_close_global_ref = get_callback_global_ref(env, on_close)?; - let reliability = decode_reliability(reliability)?; - let on_close = load_on_close(&java_vm, on_close_global_ref); - - let session = Arc::from_raw(session_ptr); - let key_expr = Arc::from_raw(key_expr_ptr); - let key_expr_clone = key_expr.deref().clone(); - tracing::debug!("Declaring subscriber on '{}'...", key_expr); - let result = session - .declare_subscriber(key_expr_clone.to_owned()) - .callback(move |sample| { - on_close.noop(); // Does nothing, but moves `on_close` inside the closure so it gets destroyed with the closure - let mut env = match java_vm.attach_current_thread_as_daemon() { - Ok(env) => env, - Err(err) => { - tracing::error!("Unable to attach thread for subscriber: {}", err); - return; - } - }; - - let byte_array = - match env.byte_array_from_slice(sample.value.payload.contiguous().as_ref()) { - Ok(byte_array) => byte_array, - Err(err) => { - tracing::error!("On subscriber callback error: {}", err.to_string()); - return; - } - }; - - let encoding: jint = sample.value.encoding.prefix().to_owned() as jint; - let kind = sample.kind.to_owned() as jint; - let (timestamp, is_valid) = sample.timestamp.map_or_else( - || (0, false), - |timestamp| (timestamp.get_time().as_u64(), true), - ); - - let attachment_bytes = match sample.attachment.map_or_else( - || env.byte_array_from_slice(&[]), - |attachment| env.byte_array_from_slice(attachment_to_vec(attachment).as_slice()), - ) { - Ok(byte_array) => byte_array, - Err(err) => { - tracing::error!( - "On subscriber callback error. Error processing attachment: {}.", - err.to_string() - ); - return; - } - }; - - let key_expr_ptr = Arc::into_raw(Arc::new(sample.key_expr)); - match env.call_method( - &callback_global_ref, - "run", - "(J[BIIJZ[B)V", - &[ - JValue::from(key_expr_ptr as jlong), - JValue::from(&byte_array), - JValue::from(encoding), - JValue::from(kind), - JValue::from(timestamp as i64), - JValue::from(is_valid), - JValue::from(&attachment_bytes), - ], - ) { - Ok(_) => {} - Err(err) => { - tracing::error!("On subscriber callback error: {}", err.to_string()); - Arc::from_raw(key_expr_ptr); // Free key expr pointer - } - }; - _ = env - .delete_local_ref(byte_array) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - _ = env - .delete_local_ref(attachment_bytes) - .map_err(|err| tracing::debug!("Error deleting local ref: {}", err)); - }) - .reliability(reliability) - .res(); - std::mem::forget(session); - std::mem::forget(key_expr); - - let subscriber = - result.map_err(|err| Error::Session(format!("Unable to declare subscriber: {}", err)))?; - - tracing::debug!( - "Subscriber declared on '{}' with reliability '{:?}'.", - key_expr_clone, - reliability, - ); - Ok(Arc::into_raw(Arc::new(subscriber))) -} - -fn decode_reliability(reliability: jint) -> Result { - match reliability { - 0 => Ok(Reliability::BestEffort), - 1 => Ok(Reliability::Reliable), - value => Err(Error::Session(format!( - "Unable to decode reliability '{value}'" - ))), - } + Arc::from_raw(subscriber_ptr); } diff --git a/zenoh-jni/src/utils.rs b/zenoh-jni/src/utils.rs index 963f93f..96c705f 100644 --- a/zenoh-jni/src/utils.rs +++ b/zenoh-jni/src/utils.rs @@ -14,40 +14,60 @@ use std::sync::Arc; +use crate::{ + errors::{Error, Result}, + jni_error, session_error, throw_exception, +}; use jni::{ objects::{JByteArray, JObject, JString}, + sys::jint, JNIEnv, JavaVM, }; -use zenoh::sample::{Attachment, AttachmentBuilder}; - -use crate::errors::{Error, Result}; +use zenoh::{ + bytes::{Encoding, ZBytes}, + internal::buffers::ZSlice, + pubsub::Reliability, + qos::{CongestionControl, Priority}, + query::{ConsolidationMode, QueryTarget}, +}; /// Converts a JString into a rust String. -pub(crate) fn decode_string(env: &mut JNIEnv, string: JString) -> Result { +pub(crate) fn decode_string(env: &mut JNIEnv, string: &JString) -> Result { let binding = env - .get_string(&string) - .map_err(|err| Error::Jni(format!("Error while retrieving JString: {}", err)))?; + .get_string(string) + .map_err(|err| jni_error!("Error while retrieving JString: {}", err))?; let value = binding .to_str() - .map_err(|err| Error::Jni(format!("Error decoding JString: {}", err)))?; + .map_err(|err| jni_error!("Error decoding JString: {}", err))?; Ok(value.to_string()) } +pub(crate) fn decode_encoding( + env: &mut JNIEnv, + encoding: jint, + schema: &JString, +) -> Result { + let schema: Option = if schema.is_null() { + None + } else { + Some(decode_string(env, schema)?.into_bytes().into()) + }; + let encoding_id = + u16::try_from(encoding).map_err(|err| jni_error!("Failed to decode encoding: {}", err))?; + Ok(Encoding::new(encoding_id, schema)) +} + pub(crate) fn get_java_vm(env: &mut JNIEnv) -> Result { env.get_java_vm() - .map_err(|err| Error::Jni(format!("Unable to retrieve JVM reference: {:?}", err))) + .map_err(|err| jni_error!("Unable to retrieve JVM reference: {}", err)) } pub(crate) fn get_callback_global_ref( env: &mut JNIEnv, callback: JObject, ) -> crate::errors::Result { - env.new_global_ref(callback).map_err(|err| { - Error::Jni(format!( - "Unable to get reference to the provided callback: {}", - err - )) - }) + env.new_global_ref(callback) + .map_err(|err| jni_error!("Unable to get reference to the provided callback: {}", err)) } /// Helper function to convert a JByteArray into a Vec. @@ -55,14 +75,72 @@ pub(crate) fn decode_byte_array(env: &JNIEnv<'_>, payload: JByteArray) -> Result let payload_len = env .get_array_length(&payload) .map(|length| length as usize) - .map_err(|err| Error::Jni(err.to_string()))?; + .map_err(|err| jni_error!(err))?; let mut buff = vec![0; payload_len]; env.get_byte_array_region(payload, 0, &mut buff[..]) - .map_err(|err| Error::Jni(err.to_string()))?; + .map_err(|err| jni_error!(err))?; let buff: Vec = unsafe { std::mem::transmute::, Vec>(buff) }; Ok(buff) } +pub(crate) fn decode_priority(priority: jint) -> Result { + Priority::try_from(priority as u8) + .map_err(|err| session_error!("Error retrieving priority: {}.", err)) +} + +pub(crate) fn decode_congestion_control(congestion_control: jint) -> Result { + match congestion_control { + 1 => Ok(CongestionControl::Block), + 0 => Ok(CongestionControl::Drop), + value => Err(session_error!("Unknown congestion control '{}'.", value)), + } +} + +pub(crate) fn decode_query_target(target: jint) -> Result { + match target { + 0 => Ok(QueryTarget::BestMatching), + 1 => Ok(QueryTarget::All), + 2 => Ok(QueryTarget::AllComplete), + value => Err(session_error!("Unable to decode QueryTarget '{}'.", value)), + } +} + +pub(crate) fn decode_consolidation(consolidation: jint) -> Result { + match consolidation { + 0 => Ok(ConsolidationMode::Auto), + 1 => Ok(ConsolidationMode::None), + 2 => Ok(ConsolidationMode::Monotonic), + 3 => Ok(ConsolidationMode::Latest), + value => Err(session_error!("Unable to decode consolidation '{}'", value)), + } +} + +pub(crate) fn decode_reliability(reliability: jint) -> Result { + match reliability { + 0 => Ok(Reliability::BestEffort), + 1 => Ok(Reliability::Reliable), + value => Err(session_error!("Unable to decode reliability '{}'", value)), + } +} + +pub(crate) fn bytes_to_java_array<'a>(env: &JNIEnv<'a>, slice: &ZBytes) -> Result> { + env.byte_array_from_slice( + slice + .deserialize::>() + .map_err(|err| session_error!("Unable to deserialize slice: {}", err))? + .as_ref(), + ) + .map_err(|err| jni_error!(err)) +} + +pub(crate) fn slice_to_java_string<'a>(env: &JNIEnv<'a>, slice: &ZSlice) -> Result> { + env.new_string( + String::from_utf8(slice.to_vec()) + .map_err(|err| session_error!("Unable to decode string: {}", err))?, + ) + .map_err(|err| jni_error!(err)) +} + /// A type that calls a function when dropped pub(crate) struct CallOnDrop(core::mem::MaybeUninit); impl CallOnDrop { @@ -102,75 +180,12 @@ pub(crate) fn load_on_close( Ok(_) => (), Err(err) => { _ = env.exception_describe(); - _ = Error::Jni(format!("Error while running 'onClose' callback: {}", err)) - .throw_on_jvm(&mut env) - .map_err(|err| { - tracing::error!( - "Unable to throw exception upon 'onClose' failure: {}", - err - ) - }); + throw_exception!( + env, + jni_error!("Error while running 'onClose' callback: {}", err) + ); } } } }) } - -/// This function is used in conjunction with the Kotlin function -/// `decodeAttachment(attachmentBytes: ByteArray): Attachment` which takes a byte array with the -/// format , repeating this -/// pattern for as many pairs there are in the attachment. -/// -/// The kotlin function expects both key size and value size to be i32 integers expressed with -/// little endian format. -/// -pub(crate) fn attachment_to_vec(attachment: Attachment) -> Vec { - let mut buffer: Vec = Vec::new(); - for (key, value) in attachment.iter() { - buffer.extend((key.len() as i32).to_le_bytes()); - buffer.extend(&key[..]); - buffer.extend((value.len() as i32).to_le_bytes()); - buffer.extend(&value[..]); - } - buffer -} - -/// This function is used in conjunction with the Kotlin function -/// `encodeAttachment(attachment: Attachment): ByteArray` which converts the attachment into a -/// ByteArray with the format , repeating this -/// pattern for as many pairs there are in the attachment. -/// -/// Both key size and value size are i32 integers with little endian format. -/// -pub(crate) fn vec_to_attachment(bytes: Vec) -> Attachment { - let mut builder = AttachmentBuilder::new(); - let mut idx = 0; - let i32_size = std::mem::size_of::(); - let mut slice_size; - - while idx < bytes.len() { - slice_size = i32::from_le_bytes( - bytes[idx..idx + i32_size] - .try_into() - .expect("Error decoding i32 while processing attachment."), //This error should never happen. - ); - idx += i32_size; - - let key = &bytes[idx..idx + slice_size as usize]; - idx += slice_size as usize; - - slice_size = i32::from_le_bytes( - bytes[idx..idx + i32_size] - .try_into() - .expect("Error decoding i32 while processing attachment."), //This error should never happen. - ); - idx += i32_size; - - let value = &bytes[idx..idx + slice_size as usize]; - idx += slice_size as usize; - - builder.insert(key, value); - } - - builder.build() -} diff --git a/zenoh-jni/src/value.rs b/zenoh-jni/src/value.rs deleted file mode 100644 index d3f27b9..0000000 --- a/zenoh-jni/src/value.rs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// - -use crate::{errors::Result, utils::decode_byte_array}; -use jni::{objects::JByteArray, JNIEnv}; -use zenoh::{ - buffers::{writer::Writer, ZBuf}, - prelude::{Encoding, HasWriter, KnownEncoding}, - value::Value, -}; - -pub(crate) fn build_value(payload: &[u8], encoding: KnownEncoding) -> Value { - let mut zbuf = ZBuf::default(); - let mut writer = zbuf.writer(); - _ = writer.write(payload); - Value::new(zbuf).encoding(Encoding::Exact(encoding)) -} - -pub(crate) fn decode_value(env: &JNIEnv<'_>, payload: JByteArray, encoding: i32) -> Result { - let buff = decode_byte_array(env, payload)?; - let encoding = match KnownEncoding::try_from(encoding as u8) { - Ok(encoding) => encoding, - Err(_) => { - tracing::debug!("Unable to retrieve encoding. Setting Empty encoding."); - KnownEncoding::Empty - } - }; - Ok(build_value(&buff[..], encoding)) -}